diff --git a/packages/components/package-lock.json b/packages/components/package-lock.json index 9f846b2f8b..27462fc631 100644 --- a/packages/components/package-lock.json +++ b/packages/components/package-lock.json @@ -1,12 +1,12 @@ { "name": "@labkey/components", - "version": "7.37.0", + "version": "7.37.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@labkey/components", - "version": "7.37.0", + "version": "7.37.1", "license": "SEE LICENSE IN LICENSE.txt", "dependencies": { "@hello-pangea/dnd": "18.0.1", diff --git a/packages/components/package.json b/packages/components/package.json index 64d6ad0a61..c9b665efe4 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@labkey/components", - "version": "7.37.0", + "version": "7.37.1", "description": "Components, models, actions, and utility functions for LabKey applications and pages", "sideEffects": false, "files": [ diff --git a/packages/components/releaseNotes/components.md b/packages/components/releaseNotes/components.md index 980662d135..e686b4604d 100644 --- a/packages/components/releaseNotes/components.md +++ b/packages/components/releaseNotes/components.md @@ -1,6 +1,14 @@ # @labkey/components Components, models, actions, and utility functions for LabKey applications and pages +### version 7.37.1 +*Released*: 20 May 2026 +- Molecule and PS bulk import by file + - `getGridIdsFromTransactionId` return type changed from `string[]` to `DataTypeRowIdsFromTransactionIds` (includes `rowIds`, `dataTypeRowCounts`) + - `selectGridIdsFromTransactionId` updated to match new return type + - `getSampleTypesFromTransactionIds` refactored to delegate to shared `getDataTypesFromTransactionId` helper; return type uses generic `dataTypes` field instead of `sampleTypes` + - New `getDataClassesFromTransactionIds` function — resolves DataClass names and builds per-type row counts from transaction audit IDs + ### version 7.37.0 *Released*: 15 May 2026 - Calculated Column Assistant diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts index ba0b3643db..736e0b3b0b 100644 --- a/packages/components/src/index.ts +++ b/packages/components/src/index.ts @@ -195,7 +195,6 @@ import { import { clearSelected, getGridIdsFromTransactionId, - getSampleTypesFromTransactionIds, getSelected, getSelectedDataDeprecated, incrementClientSideMetricCount, @@ -492,6 +491,7 @@ import { } from './internal/components/permissions/actions'; import { getContainersForPermission, + getDataClassesFromTransactionIds, getDataDeleteConfirmationData, getEntityTypeOptions, getExcludedDataTypeNames, @@ -500,6 +500,7 @@ import { getParentTypeDataForLineage, getSampleIdentifyingFieldGridData, getSampleOperationConfirmationData, + getSampleTypesFromTransactionIds, updateCellValuesForSampleIds, } from './internal/components/entities/actions'; import { @@ -1338,7 +1339,6 @@ export { genCellKey, GENERAL_ASSAY_PROVIDER_NAME, generateId, - stringToHtmlId, generateNameWithTimestamp, getActionErrorMessage, getActionValuesForFilterProps, @@ -1352,6 +1352,7 @@ export { getContainerFilterForLookups, getContainersForPermission, getDataClassDetails, + getDataClassesFromTransactionIds, getDataDeleteConfirmationData, getDateFNSDateFormat, getDateTimeInputOptions, @@ -1717,6 +1718,7 @@ export { StorageAmountInput, StorageStatusRenderer, STORED_AMOUNT_FIELDS, + stringToHtmlId, SVGIcon, Tab, TabbedGridPanel, diff --git a/packages/components/src/internal/actions.ts b/packages/components/src/internal/actions.ts index 77a10200dd..42c96b2864 100644 --- a/packages/components/src/internal/actions.ts +++ b/packages/components/src/internal/actions.ts @@ -43,8 +43,6 @@ import { resolveErrorMessage } from './util/messaging'; import { ViewInfo } from './ViewInfo'; import { createGridModelId } from './models'; -import { SAMPLES_KEY } from './app/constants'; -import { SCHEMAS } from './schemas'; export function selectAll( key: string, @@ -62,17 +60,30 @@ export function selectAll( }); } +export type DataTypeRowIdsFromTransactionIds = { + dataTypeRowCounts: Record; + dataTypes?: string[]; + rowIds: string[]; + typeNameRowCounts?: Record; +}; + +type GetTransactionRowIdsResponse = { + dataTypeRowCounts: Record; + rowIds: number[]; + success: boolean; +}; + export async function getGridIdsFromTransactionId( transactionAuditId: number | string, dataType: string, containerPath?: string -): Promise { - if (!transactionAuditId) return; +): Promise { + if (!transactionAuditId) return { rowIds: [], dataTypeRowCounts: {} }; const failureMsg = `There was a problem retrieving the ${dataType} from the last action.`; const errorLogMsg = `${failureMsg} (transactionAuditId = ${transactionAuditId})`; - const response = await request<{ rowIds: number[]; success: boolean }>({ + const response = await request({ url: ActionURL.buildURL('audit', 'getTransactionRowIds.api'), params: { containerFilter: getContainerFilterForFolder(containerPath), @@ -88,7 +99,11 @@ export async function getGridIdsFromTransactionId( } // The server returns numbers, so we coerce to string; If we don't, it can lead to bugs (and has). - return response.rowIds.map(rowId => rowId.toString()); + const rowIds = response.rowIds.map(rowId => rowId.toString()); + return { + rowIds, + dataTypeRowCounts: response.dataTypeRowCounts, + }; } export async function selectGridIdsFromTransactionId( @@ -97,35 +112,15 @@ export async function selectGridIdsFromTransactionId( transactionAuditId: number | string, dataType: string, actions: Actions -): Promise { - if (!transactionAuditId) return undefined; +): Promise { + if (!transactionAuditId) return { rowIds: [], dataTypeRowCounts: {} }; const modelId = createGridModelId(gridIdPrefix, schemaQuery); const selected = await getGridIdsFromTransactionId(transactionAuditId, dataType); - actions.replaceSelections(modelId, selected); + actions.replaceSelections(modelId, selected.rowIds); return selected; } -type SampleTypesFromTransactionIds = { rowIds: string[]; sampleTypes: string[] }; - -export async function getSampleTypesFromTransactionIds( - transactionAuditId: number | string -): Promise { - if (!transactionAuditId) return undefined; - - const rowIds = await getGridIdsFromTransactionId(transactionAuditId, SAMPLES_KEY); - const sampleTypes = await selectDistinctRows({ - schemaName: SCHEMAS.EXP_TABLES.MATERIALS.schemaName, - queryName: SCHEMAS.EXP_TABLES.MATERIALS.queryName, - column: 'SampleSet/Name', - filterArray: [Filter.create('RowId', rowIds, Filter.Types.IN)], - }); - return { - rowIds, - sampleTypes: sampleTypes.values, - }; -} - export interface ExportOptions { columns?: string; containerFilter?: Query.ContainerFilter; diff --git a/packages/components/src/internal/components/entities/actions.ts b/packages/components/src/internal/components/entities/actions.ts index 933aaa28e3..1397a7c80b 100644 --- a/packages/components/src/internal/components/entities/actions.ts +++ b/packages/components/src/internal/components/entities/actions.ts @@ -11,7 +11,12 @@ import { } from '@labkey/api'; import { List, Map, OrderedMap } from 'immutable'; -import { getSelected, getSelectedDataDeprecated } from '../../actions'; +import { + DataTypeRowIdsFromTransactionIds, + getGridIdsFromTransactionId, + getSelected, + getSelectedDataDeprecated, +} from '../../actions'; import { SampleOperation } from '../samples/constants'; import { SchemaQuery } from '../../../public/SchemaQuery'; @@ -34,7 +39,7 @@ import { getAppHomeFolderPath, getFolderDataExclusion, hasModule } from '../../a import { resolveErrorMessage } from '../../util/messaging'; -import { SAMPLE_MANAGER_APP_PROPERTIES } from '../../app/constants'; +import { SAMPLE_MANAGER_APP_PROPERTIES, SAMPLES_KEY, SOURCES_KEY } from '../../app/constants'; import { QueryModel } from '../../../public/QueryModel/QueryModel'; @@ -336,7 +341,7 @@ async function initParents( creationType?: EntityCreationType, isItemSamples?: boolean, targetQueryName?: string, - jobId?: string, + jobId?: string ): Promise> { const isAliquotParent = creationType === EntityCreationType.Aliquots; @@ -1367,8 +1372,7 @@ export function getSingleSampleTypeQueryInfo(sampleIds: number[] | string[]): Pr // GitHub Issue 928: Spaces not shown between text choices in identifying fields in editable grid export function getFieldDisplayValue(fieldData: any): string { const val = fieldData.formattedValue ?? fieldData.displayValue ?? fieldData.value; - if (Array.isArray(val)) - return val.join(', '); + if (Array.isArray(val)) return val.join(', '); return val; } @@ -1540,3 +1544,66 @@ export function updateCellValuesForSampleIds( } }); } + +async function getDataTypesFromTransactionId( + transactionAuditId: number | string, + auditDataType: string, + schemaQuery: SchemaQuery, + typeColumn: string +): Promise { + if (!transactionAuditId) return { rowIds: [], dataTypeRowCounts: {}, dataTypes: [] }; + + const { rowIds, dataTypeRowCounts } = await getGridIdsFromTransactionId(transactionAuditId, auditDataType); + const distinct = await selectDistinctRows({ + schemaName: schemaQuery.schemaName, + queryName: schemaQuery.queryName, + column: typeColumn, + filterArray: [Filter.create('RowId', rowIds, Filter.Types.IN)], + }); + return { rowIds, dataTypeRowCounts, dataTypes: distinct.values }; +} + +export function getSampleTypesFromTransactionIds( + transactionAuditId: number | string +): Promise { + return getDataTypesFromTransactionId( + transactionAuditId, + SAMPLES_KEY, + SCHEMAS.EXP_TABLES.MATERIALS, + 'SampleSet/Name' + ); +} + +export async function getDataClassesFromTransactionIds( + transactionAuditId: number | string +): Promise { + const results = await getDataTypesFromTransactionId( + transactionAuditId, + SOURCES_KEY, + SCHEMAS.EXP_TABLES.DATA, + 'DataClass/Name' + ); + + if (!results.rowIds.length) return { ...results, typeNameRowCounts: {} }; + + const { dataTypeRowCounts, dataTypes } = results; + const dataTypeLcMap = Object.fromEntries((dataTypes ?? []).map(dt => [dt.toLowerCase(), dt])); + + const typeNameRowCounts: Record = {}; + if (Object.keys(dataTypeRowCounts).length > 0) { + const dataClasses = await selectRows({ + schemaQuery: SCHEMAS.EXP_TABLES.DATA_CLASSES, + columns: ['Name', 'RowId'], + filterArray: [Filter.create('rowId', Object.keys(dataTypeRowCounts), Filter.Types.IN)], + containerFilter: Query.containerFilter.currentPlusProjectAndShared, + }); + + dataClasses.rows.forEach(row => { + const dataClassLc = caseInsensitive(row, 'Name')?.value?.toLowerCase(); + const rowId = caseInsensitive(row, 'RowId').value; + typeNameRowCounts[dataTypeLcMap[dataClassLc]] = dataTypeRowCounts[rowId]; + }); + } + + return { ...results, typeNameRowCounts }; +} diff --git a/packages/components/src/internal/components/entities/models.ts b/packages/components/src/internal/components/entities/models.ts index 9907edd40d..fc52e63d17 100644 --- a/packages/components/src/internal/components/entities/models.ts +++ b/packages/components/src/internal/components/entities/models.ts @@ -479,6 +479,7 @@ export interface EntityDataType { ancestorColumnName?: string; appUrlPrefixParts?: string[]; containerFilter?: Query.ContainerFilter; + crossFolderImportForbidden?: boolean; deleteHelpLinkTopic: string; dependencyText: Function | string; descriptionPlural: string;