From 7db4f34ddde3b18cc2b825c817e8cccf8394f7ae Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Fri, 14 Feb 2025 20:34:59 -0600 Subject: [PATCH 1/6] add LONG to types that get numeric operators --- src/qqq/components/query/FilterCriteriaRow.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qqq/components/query/FilterCriteriaRow.tsx b/src/qqq/components/query/FilterCriteriaRow.tsx index a180a1e..d35d046 100644 --- a/src/qqq/components/query/FilterCriteriaRow.tsx +++ b/src/qqq/components/query/FilterCriteriaRow.tsx @@ -109,6 +109,7 @@ export const getOperatorOptions = (tableMetaData: QTableMetaData, fieldName: str { case QFieldType.DECIMAL: case QFieldType.INTEGER: + case QFieldType.LONG: operatorOptions.push({label: "equals", value: QCriteriaOperator.EQUALS, valueMode: ValueMode.SINGLE}); operatorOptions.push({label: "does not equal", value: QCriteriaOperator.NOT_EQUALS_OR_IS_NULL, valueMode: ValueMode.SINGLE}); operatorOptions.push({label: "greater than", value: QCriteriaOperator.GREATER_THAN, valueMode: ValueMode.SINGLE}); From c69a4b8203787f40042f8c8424148d635f268f47 Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Fri, 14 Feb 2025 20:36:49 -0600 Subject: [PATCH 2/6] Make variants work for blob/download fields --- .../widgets/misc/RecordGridWidget.tsx | 2 +- src/qqq/pages/records/query/RecordQuery.tsx | 2 +- src/qqq/pages/records/view/RecordView.tsx | 6 ++--- src/qqq/utils/DataGridUtils.tsx | 23 +++++++++++++++---- src/qqq/utils/qqq/ValueUtils.tsx | 20 ++++++++++++---- 5 files changed, 40 insertions(+), 13 deletions(-) diff --git a/src/qqq/components/widgets/misc/RecordGridWidget.tsx b/src/qqq/components/widgets/misc/RecordGridWidget.tsx index f9cd56e..2c90096 100644 --- a/src/qqq/components/widgets/misc/RecordGridWidget.tsx +++ b/src/qqq/components/widgets/misc/RecordGridWidget.tsx @@ -111,7 +111,7 @@ function RecordGridWidget({widgetMetaData, data, addNewRecordCallback, disableRo } const tableMetaData = data.childTableMetaData instanceof QTableMetaData ? data.childTableMetaData as QTableMetaData : new QTableMetaData(data.childTableMetaData); - const rows = DataGridUtils.makeRows(records, tableMetaData, true); + const rows = DataGridUtils.makeRows(records, tableMetaData); ///////////////////////////////////////////////////////////////////////////////// // note - tablePath may be null, if the user doesn't have access to the table. // diff --git a/src/qqq/pages/records/query/RecordQuery.tsx b/src/qqq/pages/records/query/RecordQuery.tsx index 6efcd7a..c26fe8c 100644 --- a/src/qqq/pages/records/query/RecordQuery.tsx +++ b/src/qqq/pages/records/query/RecordQuery.tsx @@ -1103,7 +1103,7 @@ const RecordQuery = forwardRef(({table, usage, isModal, isPreview, allowVariable //////////////////////////////// // make the rows for the grid // //////////////////////////////// - const rows = DataGridUtils.makeRows(results, tableMetaData); + const rows = DataGridUtils.makeRows(results, tableMetaData, tableVariant); setRows(rows); setLoading(false); diff --git a/src/qqq/pages/records/view/RecordView.tsx b/src/qqq/pages/records/view/RecordView.tsx index ca7789c..41cf916 100644 --- a/src/qqq/pages/records/view/RecordView.tsx +++ b/src/qqq/pages/records/view/RecordView.tsx @@ -92,7 +92,7 @@ const TABLE_VARIANT_LOCAL_STORAGE_KEY_ROOT = "qqq.tableVariant"; /******************************************************************************* ** *******************************************************************************/ -export function renderSectionOfFields(key: string, fieldNames: string[], tableMetaData: QTableMetaData, helpHelpActive: boolean, record: QRecord, fieldMap?: { [name: string]: QFieldMetaData }, styleOverrides?: {label?: SxProps, value?: SxProps}) +export function renderSectionOfFields(key: string, fieldNames: string[], tableMetaData: QTableMetaData, helpHelpActive: boolean, record: QRecord, fieldMap?: { [name: string]: QFieldMetaData }, styleOverrides?: {label?: SxProps, value?: SxProps}, tableVariant?: QTableVariant) { return { @@ -118,7 +118,7 @@ export function renderSectionOfFields(key: string, fieldNames: string[], tableMe }
 
- {ValueUtils.getDisplayValue(field, record, "view", fieldName)} + {ValueUtils.getDisplayValue(field, record, "view", fieldName, tableVariant)}
@@ -597,7 +597,7 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX. // for a section with field names, render the field values. // // for the T1 section, the "wrapper" will come out below - but for other sections, produce a wrapper too. // //////////////////////////////////////////////////////////////////////////////////////////////////////////// - const fields = renderSectionOfFields(section.name, section.fieldNames, tableMetaData, helpHelpActive, record); + const fields = renderSectionOfFields(section.name, section.fieldNames, tableMetaData, helpHelpActive, record, undefined, undefined, tableVariant); if (section.tier === "T1") { diff --git a/src/qqq/utils/DataGridUtils.tsx b/src/qqq/utils/DataGridUtils.tsx index 2ee5343..b7effe7 100644 --- a/src/qqq/utils/DataGridUtils.tsx +++ b/src/qqq/utils/DataGridUtils.tsx @@ -24,6 +24,7 @@ import {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QF import {QFieldType} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldType"; import {QInstance} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QInstance"; import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData"; +import {QTableVariant} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableVariant"; import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord"; import Tooltip from "@mui/material/Tooltip/Tooltip"; import {GridColDef, GridRowsProp, MuiEvent} from "@mui/x-data-grid-pro"; @@ -70,7 +71,7 @@ export default class DataGridUtils /******************************************************************************* ** *******************************************************************************/ - public static makeRows = (results: QRecord[], tableMetaData: QTableMetaData, allowEmptyId = false): GridRowsProp[] => + public static makeRows = (results: QRecord[], tableMetaData: QTableMetaData, tableVariant?: QTableVariant): GridRowsProp[] => { const fields = [...tableMetaData.fields.values()]; const rows = [] as any[]; @@ -82,7 +83,7 @@ export default class DataGridUtils fields.forEach((field) => { - row[field.name] = ValueUtils.getDisplayValue(field, record, "query"); + row[field.name] = ValueUtils.getDisplayValue(field, record, "query", undefined, tableVariant); }); if (tableMetaData.exposedJoins) @@ -97,7 +98,7 @@ export default class DataGridUtils fields.forEach((field) => { let fieldName = join.joinTable.name + "." + field.name; - row[fieldName] = ValueUtils.getDisplayValue(field, record, "query", fieldName); + row[fieldName] = ValueUtils.getDisplayValue(field, record, "query", fieldName, tableVariant); }); } } @@ -111,7 +112,7 @@ export default class DataGridUtils ///////////////////////////////////////////////////////////////////////////////////////// // DataGrid gets very upset about a null or undefined here, so, try to make it happier // ///////////////////////////////////////////////////////////////////////////////////////// - if (!allowEmptyId) + if (!tableVariant) { row["id"] = "--"; } @@ -241,6 +242,20 @@ export default class DataGridUtils cellValues.value ? e.stopPropagation()}>{cellValues.value} : "" ); } + /* todo wip ... not sure if/how to get tooltipFieldName set in the row... */ + /* + else if (field.hasAdornment(AdornmentType.TOOLTIP)) + { + const tooltipAdornment = field.getAdornment(AdornmentType.TOOLTIP); + const tooltipFieldName: string = tooltipAdornment.getValue("tooltipFieldName"); + if(tooltipFieldName) + { + column.renderCell = (cellValues: any) => ( + cellValues.value ? {cellValues.value} : "" + ); + } + } + */ }); } diff --git a/src/qqq/utils/qqq/ValueUtils.tsx b/src/qqq/utils/qqq/ValueUtils.tsx index 2339c5b..086ea15 100644 --- a/src/qqq/utils/qqq/ValueUtils.tsx +++ b/src/qqq/utils/qqq/ValueUtils.tsx @@ -23,6 +23,7 @@ import {AdornmentType} from "@kingsrook/qqq-frontend-core/lib/model/metaData/Ado import {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData"; import {QFieldType} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldType"; import {QInstance} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QInstance"; +import {QTableVariant} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableVariant"; import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord"; import "datejs"; // https://github.com/datejs/Datejs import {Chip, ClickAwayListener, Icon} from "@mui/material"; @@ -76,14 +77,14 @@ class ValueUtils ** When you have a field, and a record - call this method to get a string or ** element back to display the field's value. *******************************************************************************/ - public static getDisplayValue(field: QFieldMetaData, record: QRecord, usage: "view" | "query" = "view", overrideFieldName?: string): string | JSX.Element | JSX.Element[] + public static getDisplayValue(field: QFieldMetaData, record: QRecord, usage: "view" | "query" = "view", overrideFieldName?: string, tableVariant?: QTableVariant): string | JSX.Element | JSX.Element[] { const fieldName = overrideFieldName ?? field.name; const displayValue = record.displayValues ? record.displayValues.get(fieldName) : undefined; const rawValue = record.values ? record.values.get(fieldName) : undefined; - return ValueUtils.getValueForDisplay(field, rawValue, displayValue, usage); + return ValueUtils.getValueForDisplay(field, rawValue, displayValue, usage, tableVariant); } @@ -91,7 +92,7 @@ class ValueUtils ** When you have a field and a value (either just a raw value, or a raw and ** display value), call this method to get a string Element to display. *******************************************************************************/ - public static getValueForDisplay(field: QFieldMetaData, rawValue: any, displayValue: any = rawValue, usage: "view" | "query" = "view"): string | JSX.Element | JSX.Element[] + public static getValueForDisplay(field: QFieldMetaData, rawValue: any, displayValue: any = rawValue, usage: "view" | "query" = "view", tableVariant?: QTableVariant): string | JSX.Element | JSX.Element[] { if (field.hasAdornment(AdornmentType.LINK)) { @@ -199,9 +200,20 @@ class ValueUtils if (field.type == QFieldType.BLOB || field.hasAdornment(AdornmentType.FILE_DOWNLOAD)) { - return (); + let url = rawValue; + if(tableVariant) + { + url += "?tableVariant=" + encodeURIComponent(JSON.stringify(tableVariant)); + } + + return (); } + // todo if(field.hasAdornment(AdornmentType.CODE)) + // todo { + // todo return {ValueUtils.getUnadornedValueForDisplay(field, rawValue, displayValue)} + // todo } + return (ValueUtils.getUnadornedValueForDisplay(field, rawValue, displayValue)); } From 44a88102605ece976d8b2707d494127f111a2da4 Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Fri, 14 Feb 2025 20:37:12 -0600 Subject: [PATCH 3/6] Remove textTransform="capitalize" from pageHeader h3 --- src/qqq/components/horseshoe/NavBar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qqq/components/horseshoe/NavBar.tsx b/src/qqq/components/horseshoe/NavBar.tsx index 24e3f94..61bf807 100644 --- a/src/qqq/components/horseshoe/NavBar.tsx +++ b/src/qqq/components/horseshoe/NavBar.tsx @@ -270,7 +270,7 @@ function NavBar({absolute, light, isMini}: Props): JSX.Element { pageHeader && - + {pageHeader} From 3bb8451671c7bdd6bc56d6f7ece9cf8c13447b92 Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Mon, 24 Feb 2025 11:10:36 -0600 Subject: [PATCH 4/6] add support for `toRecordFromTableDynamic` in LINK adornment, and `downloadUrlDynamic` in FILE_DOWNLOAD adornment --- src/qqq/utils/qqq/ValueUtils.tsx | 54 +++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/src/qqq/utils/qqq/ValueUtils.tsx b/src/qqq/utils/qqq/ValueUtils.tsx index 086ea15..c75d6e3 100644 --- a/src/qqq/utils/qqq/ValueUtils.tsx +++ b/src/qqq/utils/qqq/ValueUtils.tsx @@ -84,7 +84,7 @@ class ValueUtils const displayValue = record.displayValues ? record.displayValues.get(fieldName) : undefined; const rawValue = record.values ? record.values.get(fieldName) : undefined; - return ValueUtils.getValueForDisplay(field, rawValue, displayValue, usage, tableVariant); + return ValueUtils.getValueForDisplay(field, rawValue, displayValue, usage, tableVariant, record, fieldName); } @@ -92,14 +92,35 @@ class ValueUtils ** When you have a field and a value (either just a raw value, or a raw and ** display value), call this method to get a string Element to display. *******************************************************************************/ - public static getValueForDisplay(field: QFieldMetaData, rawValue: any, displayValue: any = rawValue, usage: "view" | "query" = "view", tableVariant?: QTableVariant): string | JSX.Element | JSX.Element[] + public static getValueForDisplay(field: QFieldMetaData, rawValue: any, displayValue: any = rawValue, usage: "view" | "query" = "view", tableVariant?: QTableVariant, record?: QRecord, fieldName?: string): string | JSX.Element | JSX.Element[] { if (field.hasAdornment(AdornmentType.LINK)) { const adornment = field.getAdornment(AdornmentType.LINK); - let href = rawValue; + let href = String(rawValue); + + let toRecordFromTable = adornment.getValue("toRecordFromTable"); + + ///////////////////////////////////////////////////////////////////////////////////// + // if the link adornment has a 'toRecordFromTableDynamic', then look for a display // + // value named `fieldName`:toRecordFromTableDynamic for the table name. // + ///////////////////////////////////////////////////////////////////////////////////// + if(adornment.getValue("toRecordFromTableDynamic")) + { + const toRecordFromTableDynamic = record?.displayValues?.get(fieldName + ":toRecordFromTableDynamic"); + if(toRecordFromTableDynamic) + { + toRecordFromTable = toRecordFromTableDynamic; + } + else + { + /////////////////////////////////////////////////////////////////// + // if the table name isn't known, then return w/o the adornment. // + /////////////////////////////////////////////////////////////////// + return (ValueUtils.getUnadornedValueForDisplay(field, rawValue, displayValue)); + } + } - const toRecordFromTable = adornment.getValue("toRecordFromTable"); if (toRecordFromTable) { if (ValueUtils.getQInstance()) @@ -108,7 +129,7 @@ class ValueUtils if (!tablePath) { console.log("Couldn't find path for table: " + toRecordFromTable); - return (displayValue ?? rawValue); + return (ValueUtils.getUnadornedValueForDisplay(field, rawValue, displayValue)); } if (!tablePath.endsWith("/")) @@ -206,6 +227,28 @@ class ValueUtils url += "?tableVariant=" + encodeURIComponent(JSON.stringify(tableVariant)); } + ////////////////////////////////////////////////////////////////////////////// + // if the field has the download adornment with a downloadUrlDynamic value, // + // then get the url from a displayValue of `fieldName`:downloadUrlDynamic. // + ////////////////////////////////////////////////////////////////////////////// + if(field.hasAdornment(AdornmentType.FILE_DOWNLOAD)) + { + const adornment = field.getAdornment(AdornmentType.FILE_DOWNLOAD); + let downloadUrlDynamicAdornmentValue = adornment.getValue("downloadUrlDynamic"); + const downloadUrlDynamicValue = record?.displayValues?.get(fieldName + ":downloadUrlDynamic"); + if (downloadUrlDynamicAdornmentValue && downloadUrlDynamicValue) + { + url = downloadUrlDynamicValue; + } + else + { + //////////////////////////////////////////////////////////////// + // if the url isn't available, then return w/o the adornment. // + //////////////////////////////////////////////////////////////// + return (ValueUtils.getUnadornedValueForDisplay(field, rawValue, displayValue)); + } + } + return (); } @@ -217,6 +260,7 @@ class ValueUtils return (ValueUtils.getUnadornedValueForDisplay(field, rawValue, displayValue)); } + /******************************************************************************* ** After we know there's no element to be returned (e.g., because no adornment), ** this method does the string formatting. From 07cb6fd323c47fac0a38dad27431a880637d4316 Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Tue, 25 Feb 2025 10:55:49 -0600 Subject: [PATCH 5/6] Fix show blob download urls when not using dynamic url --- src/qqq/utils/qqq/ValueUtils.tsx | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/qqq/utils/qqq/ValueUtils.tsx b/src/qqq/utils/qqq/ValueUtils.tsx index c75d6e3..73fa993 100644 --- a/src/qqq/utils/qqq/ValueUtils.tsx +++ b/src/qqq/utils/qqq/ValueUtils.tsx @@ -235,17 +235,20 @@ class ValueUtils { const adornment = field.getAdornment(AdornmentType.FILE_DOWNLOAD); let downloadUrlDynamicAdornmentValue = adornment.getValue("downloadUrlDynamic"); - const downloadUrlDynamicValue = record?.displayValues?.get(fieldName + ":downloadUrlDynamic"); - if (downloadUrlDynamicAdornmentValue && downloadUrlDynamicValue) + if(downloadUrlDynamicAdornmentValue) { - url = downloadUrlDynamicValue; - } - else - { - //////////////////////////////////////////////////////////////// - // if the url isn't available, then return w/o the adornment. // - //////////////////////////////////////////////////////////////// - return (ValueUtils.getUnadornedValueForDisplay(field, rawValue, displayValue)); + const downloadUrlDynamicValue = record?.displayValues?.get(fieldName + ":downloadUrlDynamic"); + if (downloadUrlDynamicValue) + { + url = downloadUrlDynamicValue; + } + else + { + //////////////////////////////////////////////////////////////// + // if the url isn't available, then return w/o the adornment. // + //////////////////////////////////////////////////////////////// + return (ValueUtils.getUnadornedValueForDisplay(field, rawValue, displayValue)); + } } } From 8ec0ce545519e657e04537a2dd8554d77379a430 Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Wed, 5 Mar 2025 19:30:52 -0600 Subject: [PATCH 6/6] Cleanup from code review --- .../widgets/misc/RecordGridWidget.tsx | 8 ++++---- src/qqq/utils/DataGridUtils.tsx | 18 ++---------------- src/qqq/utils/qqq/ValueUtils.tsx | 5 ----- 3 files changed, 6 insertions(+), 25 deletions(-) diff --git a/src/qqq/components/widgets/misc/RecordGridWidget.tsx b/src/qqq/components/widgets/misc/RecordGridWidget.tsx index 2c90096..5f9e8a6 100644 --- a/src/qqq/components/widgets/misc/RecordGridWidget.tsx +++ b/src/qqq/components/widgets/misc/RecordGridWidget.tsx @@ -111,7 +111,7 @@ function RecordGridWidget({widgetMetaData, data, addNewRecordCallback, disableRo } const tableMetaData = data.childTableMetaData instanceof QTableMetaData ? data.childTableMetaData as QTableMetaData : new QTableMetaData(data.childTableMetaData); - const rows = DataGridUtils.makeRows(records, tableMetaData); + const rows = DataGridUtils.makeRows(records, tableMetaData, undefined, true); ///////////////////////////////////////////////////////////////////////////////// // note - tablePath may be null, if the user doesn't have access to the table. // @@ -255,14 +255,14 @@ function RecordGridWidget({widgetMetaData, data, addNewRecordCallback, disableRo disabledFields = data.defaultValuesForNewChildRecords; } - const defaultValuesForNewChildRecords = data.defaultValuesForNewChildRecords || {} + const defaultValuesForNewChildRecords = data.defaultValuesForNewChildRecords || {}; /////////////////////////////////////////////////////////////////////////////////////// // copy values from specified fields in the parent record down into the child record // /////////////////////////////////////////////////////////////////////////////////////// - if(data.defaultValuesForNewChildRecordsFromParentFields) + if (data.defaultValuesForNewChildRecordsFromParentFields) { - for(let childField in data.defaultValuesForNewChildRecordsFromParentFields) + for (let childField in data.defaultValuesForNewChildRecordsFromParentFields) { const parentField = data.defaultValuesForNewChildRecordsFromParentFields[childField]; defaultValuesForNewChildRecords[childField] = parentRecord?.values?.get(parentField); diff --git a/src/qqq/utils/DataGridUtils.tsx b/src/qqq/utils/DataGridUtils.tsx index b7effe7..9ee5fa4 100644 --- a/src/qqq/utils/DataGridUtils.tsx +++ b/src/qqq/utils/DataGridUtils.tsx @@ -71,7 +71,7 @@ export default class DataGridUtils /******************************************************************************* ** *******************************************************************************/ - public static makeRows = (results: QRecord[], tableMetaData: QTableMetaData, tableVariant?: QTableVariant): GridRowsProp[] => + public static makeRows = (results: QRecord[], tableMetaData: QTableMetaData, tableVariant?: QTableVariant, allowEmptyId = false): GridRowsProp[] => { const fields = [...tableMetaData.fields.values()]; const rows = [] as any[]; @@ -112,7 +112,7 @@ export default class DataGridUtils ///////////////////////////////////////////////////////////////////////////////////////// // DataGrid gets very upset about a null or undefined here, so, try to make it happier // ///////////////////////////////////////////////////////////////////////////////////////// - if (!tableVariant) + if (!allowEmptyId) { row["id"] = "--"; } @@ -242,20 +242,6 @@ export default class DataGridUtils cellValues.value ? e.stopPropagation()}>{cellValues.value} : "" ); } - /* todo wip ... not sure if/how to get tooltipFieldName set in the row... */ - /* - else if (field.hasAdornment(AdornmentType.TOOLTIP)) - { - const tooltipAdornment = field.getAdornment(AdornmentType.TOOLTIP); - const tooltipFieldName: string = tooltipAdornment.getValue("tooltipFieldName"); - if(tooltipFieldName) - { - column.renderCell = (cellValues: any) => ( - cellValues.value ? {cellValues.value} : "" - ); - } - } - */ }); } diff --git a/src/qqq/utils/qqq/ValueUtils.tsx b/src/qqq/utils/qqq/ValueUtils.tsx index 73fa993..3d90a67 100644 --- a/src/qqq/utils/qqq/ValueUtils.tsx +++ b/src/qqq/utils/qqq/ValueUtils.tsx @@ -255,11 +255,6 @@ class ValueUtils return (); } - // todo if(field.hasAdornment(AdornmentType.CODE)) - // todo { - // todo return {ValueUtils.getUnadornedValueForDisplay(field, rawValue, displayValue)} - // todo } - return (ValueUtils.getUnadornedValueForDisplay(field, rawValue, displayValue)); }