diff --git a/src/common/components/grid/tooltip-renderer/name.tooltip-renderer.css b/src/common/components/grid/tooltip-renderer/name.tooltip-renderer.css index 650ff027..547eeef7 100644 --- a/src/common/components/grid/tooltip-renderer/name.tooltip-renderer.css +++ b/src/common/components/grid/tooltip-renderer/name.tooltip-renderer.css @@ -1,10 +1,7 @@ .layers-result-custom-tooltip { position: absolute; - height: 30px; border: 1px solid cornflowerblue; - /* overflow: hidden; */ - pointer-events: none; - transition: opacity 1s; + border-radius: 8px; } .layers-result-custom-tooltip.ag-tooltip-hiding { @@ -16,6 +13,7 @@ white-space: nowrap; } -.layers-result-custom-tooltip p:first-of-type { +.layers-result-custom-tooltip span { font-weight: bold; + padding: 0 4px; } diff --git a/src/common/components/grid/tooltip-renderer/name.tooltip-renderer.tsx b/src/common/components/grid/tooltip-renderer/name.tooltip-renderer.tsx index d939676c..1b8892d6 100644 --- a/src/common/components/grid/tooltip-renderer/name.tooltip-renderer.tsx +++ b/src/common/components/grid/tooltip-renderer/name.tooltip-renderer.tsx @@ -1,15 +1,22 @@ -import React, { forwardRef, useImperativeHandle, useState } from 'react'; +import { forwardRef, useImperativeHandle, useState } from 'react'; +import { FormattedMessage } from 'react-intl'; import { get } from 'lodash'; import { ITooltipParams } from 'ag-grid-community'; +import { Typography } from '@map-colonies/react-core'; +import { LayerRecordTypes } from '../../../../discrete-layer/components/layer-details/entity-types-keys'; +import { DateGranularityType, FieldConfigModelType } from '../../../../discrete-layer/models'; import { ILayerImage } from '../../../../discrete-layer/models/layerImage'; +import { dateFormatter } from '../../../helpers/formatters'; import './name.tooltip-renderer.css'; export default forwardRef((props: ITooltipParams, ref) => { - // eslint-disable-next-line - const [data, setData] = useState(props.api.getDisplayedRowAtIndex(props.rowIndex).data); - const color = get(props, 'color', 'white') as string; - + const [data] = useState(props.api.getDisplayedRowAtIndex(props.rowIndex).data); + const [layerRecordTypename] = useState(data.__typename); + const [color] = useState(get(props, 'color', 'white')); + const [infoTooltipMap] = useState>(get(props, 'infoTooltipMap')); + const [fields] = useState(infoTooltipMap.get(layerRecordTypename) as FieldConfigModelType[]); + useImperativeHandle(ref, () => { return { // eslint-disable-next-line @@ -20,19 +27,20 @@ export default forwardRef((props: ITooltipParams, ref) => { }); return ( -
-

- {data.productName} -

- {/*

- Country: {data.country} -

-

- Total: {data.total} -

*/} +
+ <> + { + fields.map((field: FieldConfigModelType, index: number) => { + const value = `${get(data, field.fieldName as string)}`; + return ( + + : + {field.dateGranularity ? dateFormatter(value, field.dateGranularity === DateGranularityType.DATE_AND_TIME) : value} + + ); + }) + } +
); }); \ No newline at end of file diff --git a/src/common/i18n/en.json b/src/common/i18n/en.json index e8827a76..e8844741 100644 --- a/src/common/i18n/en.json +++ b/src/common/i18n/en.json @@ -146,6 +146,7 @@ "filters.ingestion-date.unified.label": "Version Date", "results.fields.name.label": "Name", + "results.fields.ingestion-date.label": "Version Date", "results.fields.update-date.label": "Updated in catalog", "results.fields.order.label": "Order", "results.fields.resolution.label": "Resolution", diff --git a/src/common/i18n/he.json b/src/common/i18n/he.json index 4ae98eee..d48d6d4b 100644 --- a/src/common/i18n/he.json +++ b/src/common/i18n/he.json @@ -148,6 +148,7 @@ "filters.ingestion-date.unified.label": "תאריך גירסה", "results.fields.name.label": "שם", + "results.fields.ingestion-date.label": "תאריך גירסה", "results.fields.update-date.label": "עדכון פרטים", "results.fields.order.label": "סדר", "results.fields.resolution.label": "רזולוציה", diff --git a/src/discrete-layer/components/layer-details/entity-types-keys.ts b/src/discrete-layer/components/layer-details/entity-types-keys.ts index 305f46f8..f07d1fad 100644 --- a/src/discrete-layer/components/layer-details/entity-types-keys.ts +++ b/src/discrete-layer/components/layer-details/entity-types-keys.ts @@ -46,3 +46,18 @@ export const FieldConfigModelKeys: FieldConfigModelArray = Object.keys(FieldConf export type LayerMetadataMixedUnionKeys = KeysOfUnion; export type LayerRecordTypes = "Layer3DRecord" | "LayerRasterRecord" | "BestRecord" | "LayerDemRecord" | "VectorBestRecord" | "QuantizedMeshBestRecord"; + +let tempLayerRecordTypesObject: + | { + [key in LayerRecordTypes]: undefined; + } + | undefined = { + Layer3DRecord: undefined, + LayerRasterRecord: undefined, + BestRecord: undefined, + LayerDemRecord: undefined, + VectorBestRecord: undefined, + QuantizedMeshBestRecord: undefined, + }; + +export const LayerRecordTypesKeys = Object.keys(tempLayerRecordTypesObject); diff --git a/src/discrete-layer/components/layer-details/entity.dialog.tsx b/src/discrete-layer/components/layer-details/entity.dialog.tsx index 129823bb..e61a5f1c 100644 --- a/src/discrete-layer/components/layer-details/entity.dialog.tsx +++ b/src/discrete-layer/components/layer-details/entity.dialog.tsx @@ -44,6 +44,7 @@ import { Layer3DRecordModelKeys, LayerDemRecordModelKeys, LayerRasterRecordModelKeys, + LayerRecordTypes, } from './entity-types-keys'; import { LayersDetailsComponent } from './layer-details'; import { IRecordFieldInfo } from './layer-details.field-info'; @@ -74,7 +75,7 @@ interface EntityDialogProps { const setDefaultValues = (record: Record, descriptors: EntityDescriptorModelType[]): void => { getFlatEntityDescriptors( - record['__typename'] as "Layer3DRecord" | "LayerRasterRecord" | "BestRecord" | "LayerDemRecord" | "VectorBestRecord" | "QuantizedMeshBestRecord", + record['__typename'] as LayerRecordTypes, descriptors ).filter( field => field.default diff --git a/src/discrete-layer/components/layer-details/layer-datails-form.tsx b/src/discrete-layer/components/layer-details/layer-datails-form.tsx index e739f37f..63b084dd 100644 --- a/src/discrete-layer/components/layer-details/layer-datails-form.tsx +++ b/src/discrete-layer/components/layer-details/layer-datails-form.tsx @@ -30,7 +30,7 @@ import { IngestionFields } from './ingestion-fields'; import { removeEmptyObjFields, transformFormFieldsToEntity, - extractUpdateRelatedFieldNames, + extractDescriptorRelatedFieldNames, getFlatEntityDescriptors, transformEntityToFormFields, } from './utils'; @@ -190,7 +190,7 @@ const InnerForm = ( setIsSelectedFiles(!!ingestionFields.fileNames); // Check update related fields in metadata obj - const updateFields = extractUpdateRelatedFieldNames(metadata.recordModel, getFlatEntityDescriptors(layerRecord.__typename, entityDescriptors)); + const updateFields = extractDescriptorRelatedFieldNames('updateRules', getFlatEntityDescriptors(layerRecord.__typename, entityDescriptors)); for (const [key, val] of Object.entries(metadata.recordModel)) { if (val === null || (updateFields.includes(key) && mode === Mode.UPDATE)) { diff --git a/src/discrete-layer/components/layer-details/utils.ts b/src/discrete-layer/components/layer-details/utils.ts index da875076..34d4390d 100644 --- a/src/discrete-layer/components/layer-details/utils.ts +++ b/src/discrete-layer/components/layer-details/utils.ts @@ -33,13 +33,15 @@ import { Layer3DRecordModelArray, LayerDemRecordModelArray, VectorBestRecordModelArray, - QuantizedMeshBestRecordModelArray + QuantizedMeshBestRecordModelArray, + LayerRecordTypes, + LayerRecordTypesKeys } from './entity-types-keys'; const JSON_INDENTATION = 4; export const getEntityDescriptors = ( - layerRecordTypename: "Layer3DRecord" | "LayerRasterRecord" | "BestRecord" | "LayerDemRecord" | "VectorBestRecord" | "QuantizedMeshBestRecord", + layerRecordTypename: LayerRecordTypes, entityDescriptors: EntityDescriptorModelType[] ): IRecordCategoryFieldsInfo[] => { let entityDesc; @@ -67,19 +69,29 @@ export const getEntityDescriptors = ( }; export const getFlatEntityDescriptors = ( - layerRecordTypename: "Layer3DRecord" | "LayerRasterRecord" | "BestRecord" | "LayerDemRecord" | "VectorBestRecord" | "QuantizedMeshBestRecord", + layerRecordTypename: LayerRecordTypes, entityDescriptors: EntityDescriptorModelType[] ): FieldConfigModelType[] => { const descriptors = getEntityDescriptors(layerRecordTypename, entityDescriptors); - const flat: FieldConfigModelType[] = []; + let flat: FieldConfigModelType[] = []; descriptors.forEach((category: CategoryConfigModelType) => { - category.fields?.forEach((field: FieldConfigModelType) => { - flat.push(field); - }); + flat = [ ...flat, ...(category.fields ?? []) ]; }); return flat; }; +export const getFieldNamesByEntityDescriptorMap = ( + descriptor: keyof FieldConfigModelType, + entityDescriptors: EntityDescriptorModelType[] +): Map => { + const fieldNamesMap = new Map(); + LayerRecordTypesKeys.forEach((layerRecordTypename: string) => { + const fieldNames = extractDescriptorRelatedFieldNames(descriptor, getFlatEntityDescriptors(layerRecordTypename as LayerRecordTypes, entityDescriptors)); + fieldNamesMap.set(layerRecordTypename, fieldNames); + }); + return fieldNamesMap; +}; + export const getBasicType = (fieldName: FieldInfoName, typename: string, lookupTable?: string): string => { let recordModel; if (lookupTable != null && lookupTable !== 'zoomlevelresolutions') return 'LookupTableType'; @@ -252,9 +264,9 @@ export const getPartialRecord = (inputValues: Partial, descriptors: return partialRecordData; }; -export const extractUpdateRelatedFieldNames = (record: ILayerImage, descriptors: FieldConfigModelType[]): string[] => { - const updateRulesFields = descriptors.filter((descriptor) => descriptor.updateRules !== null); - return updateRulesFields.map(field => field.fieldName) as string[]; +export const extractDescriptorRelatedFieldNames = (descriptorName: keyof FieldConfigModelType, descriptors: FieldConfigModelType[]): string[] => { + const fields = descriptors.filter((descriptor) => descriptor[descriptorName]); + return fields.map(field => field.fieldName) as string[]; }; // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/src/discrete-layer/components/layers-results/layers-results.tsx b/src/discrete-layer/components/layers-results/layers-results.tsx index dc0fa425..1e1a47a7 100644 --- a/src/discrete-layer/components/layers-results/layers-results.tsx +++ b/src/discrete-layer/components/layers-results/layers-results.tsx @@ -1,6 +1,7 @@ import React, { useEffect, useMemo, useRef, useState } from 'react'; import { useIntl } from 'react-intl'; import { ChangeDetectionStrategyType } from 'ag-grid-react'; +import { ColDef, RowDataChangedEvent, ValueGetterParams } from 'ag-grid-community'; import { observer } from 'mobx-react-lite'; import { isObject, isEmpty } from 'lodash'; import { Box } from '@map-colonies/react-components'; @@ -22,18 +23,20 @@ import { ProductTypeRenderer } from '../../../common/components/grid/cell-render import { StyledByDataRenderer } from '../../../common/components/grid/cell-renderer/styled-by-data.cell-renderer'; import { HeaderFootprintRenderer } from '../../../common/components/grid/header-renderer/footprint.header-renderer'; import CustomTooltip from '../../../common/components/grid/tooltip-renderer/name.tooltip-renderer'; +import { Error } from '../../../common/components/tree/statuses/error'; +import { Loading } from '../../../common/components/tree/statuses/loading'; import CONFIG from '../../../common/config'; import { dateFormatter } from '../../../common/helpers/formatters'; import { usePrevious } from '../../../common/hooks/previous.hook'; +import { EntityDescriptorModelType } from '../../models'; import { IDispatchAction } from '../../models/actionDispatcherStore'; import { ILayerImage } from '../../models/layerImage'; import { useStore } from '../../models/RootStore'; import { TabViews } from '../../views/tab-views'; +import { LayerRecordTypes } from '../layer-details/entity-types-keys'; +import { getFieldNamesByEntityDescriptorMap } from '../layer-details/utils'; import './layers-results.css'; -import { ColDef, RowDataChangedEvent } from 'ag-grid-community'; -import { Loading } from '../../../common/components/tree/statuses/loading'; -import { Error } from '../../../common/components/tree/statuses/error'; const PAGINATION = true; const PAGE_SIZE = 10; @@ -200,18 +203,22 @@ export const LayersResultsComponent: React.FC = obs field: 'productName', cellRenderer: 'styledByDataRenderer', suppressMovable: true, - tooltipComponent: 'customTooltip', tooltipField: 'productName', - tooltipComponentParams: { color: '#ececec' } + tooltipComponent: 'customTooltip', + tooltipComponentParams: { color: '#ececec', infoTooltipMap: store.discreteLayersStore.entityTooltipFields } }, { headerName: intl.formatMessage({ - id: 'results.fields.update-date.label', + id: 'results.fields.ingestion-date.label', }), minWidth: 140, flex: 1, - field: 'updateDate', + field: 'ingestionDate', suppressMovable: true, + valueGetter: (params: ValueGetterParams): string => { + const { data } = params; + return data.ingestionDate !== undefined && data.ingestionDate !== null ? data.ingestionDate : data.insertDate; + }, valueFormatter: (params: GridValueFormatterParams): string => dateFormatter(params.value) }, { @@ -286,15 +293,15 @@ export const LayersResultsComponent: React.FC = obs onRowDataUpdated(event: RowDataChangedEvent) { const rowToUpdate: GridRowNode | undefined | null = event.api.getRowNode(store.discreteLayersStore.selectedLayer?.id as string); - // Find the pinned column to update as well. + // Find the pinned column to update as well const pinnedColId = (event.api.getColumnDefs().find(colDef => (colDef as ColDef).pinned) as ColDef).colId as string; - event.api.refreshCells({ - force: true, - suppressFlash: true, - columns:['productName', '__typename', 'updateDate', pinnedColId], - rowNodes: !isEmpty(rowToUpdate) ? [rowToUpdate] : undefined - }); + event.api.refreshCells({ + force: true, + suppressFlash: true, + columns:['productName', '__typename', 'updateDate', pinnedColId], + rowNodes: !isEmpty(rowToUpdate) ? [rowToUpdate] : undefined + }); }, }; @@ -306,23 +313,23 @@ export const LayersResultsComponent: React.FC = obs useEffect(() => { gridApi?.setColumnDefs(colDef); - },[store.userStore.user]); + }, [store.userStore.user]); return ( - - {props.searchError ? : - + + { + props.searchError ? + : + } ); diff --git a/src/discrete-layer/models/FieldConfigModel.base.ts b/src/discrete-layer/models/FieldConfigModel.base.ts index 1b99f975..1cfd0a20 100644 --- a/src/discrete-layer/models/FieldConfigModel.base.ts +++ b/src/discrete-layer/models/FieldConfigModel.base.ts @@ -47,6 +47,7 @@ export const FieldConfigModelBase = withTypedRefs()(ModelBase isManuallyEditable: types.union(types.undefined, types.null, types.boolean), isFilterable: types.union(types.undefined, types.null, types.late((): any => FilterableFieldConfigModel)), isBriefField: types.union(types.undefined, types.null, types.late((): any => BriefFieldConfigModel)), + isInfoTooltip: types.union(types.undefined, types.null, types.boolean), isSortable: types.union(types.undefined, types.null, types.boolean), isRequired: types.union(types.undefined, types.null, types.boolean), isAutoGenerated: types.union(types.undefined, types.null, types.boolean), @@ -80,6 +81,7 @@ export class FieldConfigModelSelector extends QueryBuilder { get label() { return this.__attr(`label`) } get fullWidth() { return this.__attr(`fullWidth`) } get isManuallyEditable() { return this.__attr(`isManuallyEditable`) } + get isInfoTooltip() { return this.__attr(`isInfoTooltip`) } get isSortable() { return this.__attr(`isSortable`) } get isRequired() { return this.__attr(`isRequired`) } get isAutoGenerated() { return this.__attr(`isAutoGenerated`) } @@ -106,4 +108,4 @@ export function selectFromFieldConfig() { return new FieldConfigModelSelector() } -export const fieldConfigModelPrimitives = selectFromFieldConfig().fieldName.queryableName.fullWidth.isManuallyEditable.isSortable.isRequired.isAutoGenerated.isLifecycleEnvolved.isCopyable.isDisabled.rows.infoMsgCode.default.dateGranularity.autocomplete(autocompletionModelPrimitives).validation(validationConfigModelPrimitives).isFilterable(filterableFieldConfigModelPrimitives).isBriefField(briefFieldConfigModelPrimitives).lookupTable.lookupExcludeFields.dependentField(dependentFieldModelPrimitives).lookupTableBinding(lookupTableBindingModelPrimitives) \ No newline at end of file +export const fieldConfigModelPrimitives = selectFromFieldConfig().fieldName.queryableName.fullWidth.isManuallyEditable.isInfoTooltip.isSortable.isRequired.isAutoGenerated.isLifecycleEnvolved.isCopyable.isDisabled.rows.infoMsgCode.default.dateGranularity.autocomplete(autocompletionModelPrimitives).validation(validationConfigModelPrimitives).isFilterable(filterableFieldConfigModelPrimitives).isBriefField(briefFieldConfigModelPrimitives).lookupTable.lookupExcludeFields.dependentField(dependentFieldModelPrimitives).lookupTableBinding(lookupTableBindingModelPrimitives) \ No newline at end of file diff --git a/src/discrete-layer/models/discreteLayersStore.ts b/src/discrete-layer/models/discreteLayersStore.ts index c5907add..230a6006 100644 --- a/src/discrete-layer/models/discreteLayersStore.ts +++ b/src/discrete-layer/models/discreteLayersStore.ts @@ -5,7 +5,7 @@ import intersect from '@turf/intersect'; import bboxPolygon from '@turf/bbox-polygon'; import bbox from '@turf/bbox'; import { IBaseMaps } from '@map-colonies/react-components/dist/cesium-map/settings/settings'; -import { cloneDeep, set, get, isEmpty } from 'lodash'; +import { cloneDeep, set, get } from 'lodash'; import { Geometry, Polygon } from 'geojson'; import { ApiHttpResponse } from '../../common/models/api-response'; import { ResponseState } from '../../common/models/response-state.enum'; @@ -20,8 +20,8 @@ import { EntityDescriptorModelType } from './EntityDescriptorModel'; import { isUnpublished } from '../../common/helpers/style'; import { LinkType } from '../../common/models/link-type.enum'; import { getLayerLink } from '../components/helpers/layersUtils'; -import { LayerMetadataMixedUnionKeys, LayerRecordTypes } from '../components/layer-details/entity-types-keys'; -import { getFlatEntityDescriptors } from '../components/layer-details/utils'; +import { LayerMetadataMixedUnionKeys, LayerRecordTypes, LayerRecordTypesKeys } from '../components/layer-details/entity-types-keys'; +import { extractDescriptorRelatedFieldNames, getFlatEntityDescriptors } from '../components/layer-details/utils'; import { CapabilityModelType } from './CapabilityModel'; import { FieldConfigModelType } from './FieldConfigModel'; import { RecordType } from './RecordTypeEnum'; @@ -50,6 +50,7 @@ const INITIAL_STATE = { selectedLayerIsUpdateMode: false, tabViews: [{idx: TabViews.CATALOG}, {idx: TabViews.SEARCH_RESULTS}, {idx: TabViews.CREATE_BEST}, {idx: TabViews.EXPORT_LAYER}], entityDescriptors: [], + entityTooltipFields: new Map(), previewedLayers: [], capabilities: [], baseMaps: CONFIG.BASE_MAPS, @@ -68,6 +69,7 @@ export const discreteLayersStore = ModelBase selectedLayerIsUpdateMode: types.maybe(types.frozen(INITIAL_STATE.selectedLayerIsUpdateMode)), tabViews: types.maybe(types.frozen(INITIAL_STATE.tabViews)), entityDescriptors: types.maybe(types.frozen(INITIAL_STATE.entityDescriptors)), + entityTooltipFields: types.maybe(types.frozen>(INITIAL_STATE.entityTooltipFields)), previewedLayers: types.maybe(types.frozen(INITIAL_STATE.previewedLayers)), capabilities: types.maybe(types.frozen(INITIAL_STATE.capabilities)), baseMaps: types.maybe(types.frozen(INITIAL_STATE.baseMaps)), @@ -112,6 +114,13 @@ export const discreteLayersStore = ModelBase function setEntityDescriptors(data: EntityDescriptorModelType[]): void { self.entityDescriptors = cloneDeep(data); + + LayerRecordTypesKeys.forEach((layerRecordTypename: string) => { + const flatEntityDescriptors = getFlatEntityDescriptors(layerRecordTypename as LayerRecordTypes, self.entityDescriptors as EntityDescriptorModelType[]); + const fieldNames = extractDescriptorRelatedFieldNames('isInfoTooltip', flatEntityDescriptors); + const fields: FieldConfigModelType[] = flatEntityDescriptors.filter((field: FieldConfigModelType) => fieldNames.includes(field.fieldName as string)); + self.entityTooltipFields?.set(layerRecordTypename as LayerRecordTypes, fields); + }); } function getPreparedLayersImages(data: ILayerImage[], showFootprint = true): LayersImagesResponse { diff --git a/src/discrete-layer/views/components/data-fetchers/entity-descriptors-fetcher.component.tsx b/src/discrete-layer/views/components/data-fetchers/entity-descriptors-fetcher.component.tsx index 021b4720..2ad4b20f 100644 --- a/src/discrete-layer/views/components/data-fetchers/entity-descriptors-fetcher.component.tsx +++ b/src/discrete-layer/views/components/data-fetchers/entity-descriptors-fetcher.component.tsx @@ -86,6 +86,7 @@ export const EntityDescriptorsFetcher: React.FC = observer(() => { isBriefField { order } + isInfoTooltip lookupTableBinding { valueFromPropertyName }