From c8378f852b00ca6f9154843a35552f91fb710ee8 Mon Sep 17 00:00:00 2001 From: Sovas Tiwari Date: Fri, 19 Dec 2025 15:49:43 +0545 Subject: [PATCH 1/4] Improve the clone functionality for the catalog and details page Fix the issue to display the status on refresh and call execution tracker api after refresh --- .../js/observables/persistence/index.js | 8 +- .../js/plugins/ExecutionTracker/index.jsx | 143 ++++++++++++++++++ .../client/js/plugins/SaveAs.jsx | 6 - .../client/js/plugins/index.js | 2 + .../client/js/utils/ResourceServiceUtils.js | 2 +- .../client/js/utils/ResourceUtils.js | 10 +- .../geonode/less/_execution-tracker.less | 37 +++++ .../client/themes/geonode/less/geonode.less | 2 + .../static/mapstore/configs/localConfig.json | 12 ++ 9 files changed, 209 insertions(+), 13 deletions(-) create mode 100644 geonode_mapstore_client/client/js/plugins/ExecutionTracker/index.jsx create mode 100644 geonode_mapstore_client/client/themes/geonode/less/_execution-tracker.less diff --git a/geonode_mapstore_client/client/js/observables/persistence/index.js b/geonode_mapstore_client/client/js/observables/persistence/index.js index b9d71d91ac..0dc4a84eda 100644 --- a/geonode_mapstore_client/client/js/observables/persistence/index.js +++ b/geonode_mapstore_client/client/js/observables/persistence/index.js @@ -80,9 +80,15 @@ const persistence = { config, customFilters }).then(({ resources, ...response }) => { + const filterEmptyResources = resources.filter(resource => !resource.resource_type); + const filteredResources = resources.filter(resource => resource.resource_type); + response = { + ...response, + total: response.total - filterEmptyResources.length + } return { ...response, - resources: resources.map(parseCatalogResource) + resources: filteredResources.map((resource) => parseCatalogResource(resource, monitoredState.user)) }; }); }); diff --git a/geonode_mapstore_client/client/js/plugins/ExecutionTracker/index.jsx b/geonode_mapstore_client/client/js/plugins/ExecutionTracker/index.jsx new file mode 100644 index 0000000000..23419bda26 --- /dev/null +++ b/geonode_mapstore_client/client/js/plugins/ExecutionTracker/index.jsx @@ -0,0 +1,143 @@ +/* + * Copyright 2025, GeoSolutions Sas. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React, { useEffect, useMemo, useRef } from 'react'; +import { connect } from 'react-redux'; +import { createSelector } from 'reselect'; +import { createPlugin } from '@mapstore/framework/utils/PluginsUtils'; +import { userSelector } from '@mapstore/framework/selectors/security'; +import { getResources } from '@mapstore/framework/plugins/ResourcesCatalog/selectors/resources'; + +import { startAsyncProcess } from '@js/actions/resourceservice'; +import { extractExecutionsFromResources, ProcessTypes } from '@js/utils/ResourceServiceUtils'; +import { getResourceData } from '@js/selectors/resource'; +import isEmpty from 'lodash/isEmpty'; +import { getCurrentProcesses } from '@js/selectors/resourceservice'; +import FlexBox from '@mapstore/framework/components/layout/FlexBox'; +import Spinner from '@mapstore/framework/components/layout/Spinner'; +import Message from '@mapstore/framework/components/I18N/Message'; +import ViewerLayout from '@js/components/ViewerLayout/ViewerLayout'; + +/** + * Plugin that monitors async executions embedded in resources and + * triggers the executions API using the existing resourceservice epics. + * + * It reads `resources[*].executions` and, when it finds executions and, + * dispatches `startAsyncProcess({ resource, output, processType })` once per execution. + * + */ +function ExecutionTracker({ + resources, + user, + onStartAsyncProcess, + resourceData, + processes +}) { + const startedKeys = useRef(new Set()); + const redirected = useRef(false); + + useEffect(() => { + const username = user?.info?.preferred_username; + const resourcesToTrack = !isEmpty(resourceData) ? [...resources, resourceData] : resources; + if (!resourcesToTrack?.length || !username) { + return; + } + const executions = extractExecutionsFromResources(resourcesToTrack, username) || []; + if (!executions.length) { + return; + } + executions.forEach((process) => { + const pk = process?.resource?.pk ?? process?.resource?.id; + const processType = process?.processType; + const statusUrl = process?.output?.status_url; + if (!pk || !processType || !statusUrl) { + return; + } + const key = `${pk}:${processType}:${statusUrl}`; + if (!startedKeys.current.has(key)) { + startedKeys.current.add(key); + onStartAsyncProcess(process); + } + }); + }, [resources, user, onStartAsyncProcess, resourceData]); + + useEffect(() => { + if (redirected.current) { + return; + } + const resourcePk = resourceData?.pk ?? resourceData?.id; + if (!resourcePk) { + return; + } + const clonedResourceUrl = (processes || []) + .find((p) => p?.resource?.pk === resourcePk && !!p?.clonedResourceUrl) + ?.clonedResourceUrl; + + if (clonedResourceUrl && window?.location?.href !== clonedResourceUrl) { + redirected.current = true; + window.location.assign(clonedResourceUrl); + } + }, [processes, resourceData]); + + const msgId = useMemo(() => { + if (isEmpty(resourceData)) { + return null; + } + + const foundProcess = processes.filter((p) => p?.resource?.pk === resourceData?.pk); + if (!foundProcess?.length) { + return null; + } + const copying = foundProcess.some((p) => [ProcessTypes.COPY_RESOURCE, 'copy', 'copy_geonode_resource'].includes(p?.processType)); + const deleting = foundProcess.some((p) => [ProcessTypes.DELETE_RESOURCE, 'delete'].includes(p?.processType)); + if (copying) { + return 'gnviewer.cloning'; + } + if (deleting) { + return 'gnviewer.deleting'; + } + return null; + }, [processes, resourceData]); + + return msgId ? ( +
+ +
+ + + + +
+
+
+ ) : null; +} + +const ExecutionTrackerPlugin = connect( + createSelector( + [ + (state) => getResources(state, { id: 'catalog' }), + userSelector, + getResourceData, + getCurrentProcesses + ], + (resources, user, resourceData, processes) => ({ + resources, + user, + resourceData, + processes + }) + ), + { + onStartAsyncProcess: startAsyncProcess + } +)(ExecutionTracker); + +export default createPlugin('ExecutionTracker', { + component: ExecutionTrackerPlugin +}); diff --git a/geonode_mapstore_client/client/js/plugins/SaveAs.jsx b/geonode_mapstore_client/client/js/plugins/SaveAs.jsx index d63c1ec178..55cf7341d8 100644 --- a/geonode_mapstore_client/client/js/plugins/SaveAs.jsx +++ b/geonode_mapstore_client/client/js/plugins/SaveAs.jsx @@ -36,7 +36,6 @@ import { canCopyResource } from '@js/utils/ResourceUtils'; import { processResources } from '@js/actions/gnresource'; import { getCurrentResourceCopyLoading } from '@js/selectors/resourceservice'; import withPrompt from '@js/plugins/save/withPrompt'; -import { ResourceCloningIndicator } from './ActionNavbar/buttons'; function SaveAs({ resources, @@ -218,11 +217,6 @@ export default createPlugin('SaveAs', { ActionNavbar: [{ name: 'SaveAs', Component: ConnectedSaveAsButton - }, { - name: 'ResourceCloningIndicator', - Component: ResourceCloningIndicator, - target: 'right-menu', - position: 1 }], ResourcesGrid: { name: ProcessTypes.COPY_RESOURCE, diff --git a/geonode_mapstore_client/client/js/plugins/index.js b/geonode_mapstore_client/client/js/plugins/index.js index 077962ca23..3b59751ea6 100644 --- a/geonode_mapstore_client/client/js/plugins/index.js +++ b/geonode_mapstore_client/client/js/plugins/index.js @@ -23,6 +23,7 @@ import Itinerary from "@mapstore/framework/plugins/Itinerary"; import SecurityPopup from "@mapstore/framework/plugins/SecurityPopup"; import OperationPlugin from '@js/plugins/Operation'; +import ExecutionTrackerPlugin from '@js/plugins/ExecutionTracker'; import MetadataEditorPlugin from '@js/plugins/MetadataEditor'; import MetadataViewerPlugin from '@js/plugins/MetadataEditor/MetadataViewer'; import FavoritesPlugin from '@js/plugins/Favorites'; @@ -78,6 +79,7 @@ const toModulePlugin = (...args) => { export const plugins = { TOCPlugin, OperationPlugin, + ExecutionTrackerPlugin, MetadataEditorPlugin, MetadataViewerPlugin, ResourcesGridPlugin, diff --git a/geonode_mapstore_client/client/js/utils/ResourceServiceUtils.js b/geonode_mapstore_client/client/js/utils/ResourceServiceUtils.js index 93bf1321d8..37cbfdc9d1 100644 --- a/geonode_mapstore_client/client/js/utils/ResourceServiceUtils.js +++ b/geonode_mapstore_client/client/js/utils/ResourceServiceUtils.js @@ -55,7 +55,7 @@ export const extractExecutionsFromResources = (resources, username) => { status_url: statusUrl, user }) => - funcName === 'copy' + ['copy', 'copy_geonode_resource'].includes(funcName) && statusUrl && user && user === username ).map((output) => { return { diff --git a/geonode_mapstore_client/client/js/utils/ResourceUtils.js b/geonode_mapstore_client/client/js/utils/ResourceUtils.js index 13af8b33d1..b2bab53703 100644 --- a/geonode_mapstore_client/client/js/utils/ResourceUtils.js +++ b/geonode_mapstore_client/client/js/utils/ResourceUtils.js @@ -460,11 +460,11 @@ export const getResourceStatuses = (resource, userInfo) => { const isPublished = isApproved && resource?.is_published; const runningExecutions = executions.filter(({ func_name: funcName, status, user }) => [ProcessStatus.RUNNING, ProcessStatus.READY].includes(status) - && ['delete', 'copy', ProcessTypes.DELETE_RESOURCE, ProcessTypes.COPY_RESOURCE].includes(funcName) + && ['delete', 'copy', 'copy_geonode_resource', ProcessTypes.DELETE_RESOURCE, ProcessTypes.COPY_RESOURCE].includes(funcName) && (user === undefined || user === userInfo?.info?.preferred_username)); const isProcessing = !!runningExecutions.length; const isDeleting = runningExecutions.some(({ func_name: funcName }) => ['delete', ProcessTypes.DELETE_RESOURCE].includes(funcName)); - const isCopying = runningExecutions.some(({ func_name: funcName }) => ['copy', ProcessTypes.COPY_RESOURCE].includes(funcName)); + const isCopying = runningExecutions.some(({ func_name: funcName }) => ['copy', 'copy_geonode_resource', ProcessTypes.COPY_RESOURCE].includes(funcName)); return { isApproved, isPublished, @@ -878,7 +878,7 @@ export const getResourceAdditionalProperties = (_resource = {}) => { }; }; -export const parseCatalogResource = (resource) => { +export const parseCatalogResource = (resource, user) => { const { formatDetailUrl, icon, @@ -886,7 +886,7 @@ export const parseCatalogResource = (resource) => { canPreviewed, hasPermission, name - } = getResourceTypesInfo(resource)[resource.resource_type]; + } = getResourceTypesInfo(resource)[resource.resource_type] || {}; const resourceCanPreviewed = resource?.pk && canPreviewed && canPreviewed(resource); const embedUrl = resourceCanPreviewed && formatEmbedUrl && resource?.embed_url && formatEmbedUrl(resource); const canView = resource?.pk && hasPermission && hasPermission(resource); @@ -911,7 +911,7 @@ export const parseCatalogResource = (resource) => { metadataDetailUrl, typeName: name }, - status: getResourceStatuses(resource) + status: getResourceStatuses(resource, user) } }; }; diff --git a/geonode_mapstore_client/client/themes/geonode/less/_execution-tracker.less b/geonode_mapstore_client/client/themes/geonode/less/_execution-tracker.less new file mode 100644 index 0000000000..be9225e142 --- /dev/null +++ b/geonode_mapstore_client/client/themes/geonode/less/_execution-tracker.less @@ -0,0 +1,37 @@ +#ms-components-theme(@theme-vars) { + .gn-main-execution-container { + .background-color-var(@theme-vars[main-variant-bg]); + .gn-execution-tracker-content { + .background-color-var(@theme-vars[main-variant-bg]); + } + } +} + +.gn-execution-tracker { + position: absolute; + z-index: 5000; + width: 100%; + height: 100%; + top: 0; + left: 0; + .gn-main-execution-container { + background-color: rgba(0, 0, 0, 0.85); + position: relative; + width: 100%; + height: 100%; + margin: 0; + padding: 0; + display: flex; + align-items: center; + justify-content: center; + .gn-execution-tracker-content { + width: 100%; + height: 100%; + color: #eeeeee; + background-color: transparent; + display: flex; + align-items: center; + justify-content: center; + } + } +} \ No newline at end of file diff --git a/geonode_mapstore_client/client/themes/geonode/less/geonode.less b/geonode_mapstore_client/client/themes/geonode/less/geonode.less index b37eced28d..d138442750 100644 --- a/geonode_mapstore_client/client/themes/geonode/less/geonode.less +++ b/geonode_mapstore_client/client/themes/geonode/less/geonode.less @@ -28,6 +28,8 @@ @import '~font-awesome/css/font-awesome.min.css'; @import '~ol/ol.css'; +@import '_execution-tracker.less'; + :root { font-size: 16px; } diff --git a/geonode_mapstore_client/static/mapstore/configs/localConfig.json b/geonode_mapstore_client/static/mapstore/configs/localConfig.json index 5e3ce4e6d8..b03dfc286b 100644 --- a/geonode_mapstore_client/static/mapstore/configs/localConfig.json +++ b/geonode_mapstore_client/static/mapstore/configs/localConfig.json @@ -1236,6 +1236,12 @@ }, { "name": "MetadataViewer" + }, + { + "name": "ExecutionTracker", + "cfg": { + "containerPosition": "header" + } } ], "dataset_edit_data_viewer": [ @@ -3485,6 +3491,12 @@ }, { "name": "Favorites" + }, + { + "name": "ExecutionTracker", + "cfg": { + "containerPosition": "header" + } } ], "viewer": [ From b103a8cd492e42e30b9e32f4975ddbebb2a76dc9 Mon Sep 17 00:00:00 2001 From: Sovas Tiwari Date: Fri, 19 Dec 2025 16:27:39 +0545 Subject: [PATCH 2/4] Fix the eslint and improve the resource data checks --- .../js/observables/persistence/index.js | 6 +---- .../js/plugins/ExecutionTracker/index.jsx | 20 +++++++-------- .../geonode/less/_execution-tracker.less | 25 ++++--------------- 3 files changed, 15 insertions(+), 36 deletions(-) diff --git a/geonode_mapstore_client/client/js/observables/persistence/index.js b/geonode_mapstore_client/client/js/observables/persistence/index.js index 0dc4a84eda..c8a897f5fb 100644 --- a/geonode_mapstore_client/client/js/observables/persistence/index.js +++ b/geonode_mapstore_client/client/js/observables/persistence/index.js @@ -80,14 +80,10 @@ const persistence = { config, customFilters }).then(({ resources, ...response }) => { - const filterEmptyResources = resources.filter(resource => !resource.resource_type); const filteredResources = resources.filter(resource => resource.resource_type); - response = { - ...response, - total: response.total - filterEmptyResources.length - } return { ...response, + total: response.total - (resources.length - filteredResources.length), resources: filteredResources.map((resource) => parseCatalogResource(resource, monitoredState.user)) }; }); diff --git a/geonode_mapstore_client/client/js/plugins/ExecutionTracker/index.jsx b/geonode_mapstore_client/client/js/plugins/ExecutionTracker/index.jsx index 23419bda26..1678b37ce2 100644 --- a/geonode_mapstore_client/client/js/plugins/ExecutionTracker/index.jsx +++ b/geonode_mapstore_client/client/js/plugins/ExecutionTracker/index.jsx @@ -21,7 +21,6 @@ import { getCurrentProcesses } from '@js/selectors/resourceservice'; import FlexBox from '@mapstore/framework/components/layout/FlexBox'; import Spinner from '@mapstore/framework/components/layout/Spinner'; import Message from '@mapstore/framework/components/I18N/Message'; -import ViewerLayout from '@js/components/ViewerLayout/ViewerLayout'; /** * Plugin that monitors async executions embedded in resources and @@ -88,8 +87,11 @@ function ExecutionTracker({ if (isEmpty(resourceData)) { return null; } - - const foundProcess = processes.filter((p) => p?.resource?.pk === resourceData?.pk); + const resourcePk = resourceData?.pk ?? resourceData?.id; + if (!resourcePk) { + return null; + } + const foundProcess = processes.filter((p) => p?.resource?.pk === resourcePk); if (!foundProcess?.length) { return null; } @@ -106,14 +108,10 @@ function ExecutionTracker({ return msgId ? (
- -
- - - - -
-
+ + + +
) : null; } diff --git a/geonode_mapstore_client/client/themes/geonode/less/_execution-tracker.less b/geonode_mapstore_client/client/themes/geonode/less/_execution-tracker.less index be9225e142..47f9b3e579 100644 --- a/geonode_mapstore_client/client/themes/geonode/less/_execution-tracker.less +++ b/geonode_mapstore_client/client/themes/geonode/less/_execution-tracker.less @@ -14,24 +14,9 @@ height: 100%; top: 0; left: 0; - .gn-main-execution-container { - background-color: rgba(0, 0, 0, 0.85); - position: relative; - width: 100%; - height: 100%; - margin: 0; - padding: 0; - display: flex; - align-items: center; - justify-content: center; - .gn-execution-tracker-content { - width: 100%; - height: 100%; - color: #eeeeee; - background-color: transparent; - display: flex; - align-items: center; - justify-content: center; - } - } + background-color: rgba(0, 0, 0, 0.85); + color: #eeeeee; + display: flex; + align-items: center; + justify-content: center; } \ No newline at end of file From fd3e74c3049dc36271ef5071bab4ce477a913cd9 Mon Sep 17 00:00:00 2001 From: Sovas Tiwari Date: Thu, 8 Jan 2026 13:26:12 +0545 Subject: [PATCH 3/4] Update the exeuction tracker for detail and resource page --- .../client/js/apps/gn-components.js | 4 +- .../client/js/epics/index.js | 37 ++++++++++++++++++- .../js/observables/persistence/index.js | 4 +- .../js/plugins/ExecutionTracker/index.jsx | 30 ++++++--------- .../client/js/utils/ResourceServiceUtils.js | 2 +- .../client/js/utils/ResourceUtils.js | 9 +++++ .../client/themes/geonode/less/geonode.less | 3 +- .../static/mapstore/configs/localConfig.json | 6 --- 8 files changed, 63 insertions(+), 32 deletions(-) diff --git a/geonode_mapstore_client/client/js/apps/gn-components.js b/geonode_mapstore_client/client/js/apps/gn-components.js index 983b964a2c..505dbb3214 100644 --- a/geonode_mapstore_client/client/js/apps/gn-components.js +++ b/geonode_mapstore_client/client/js/apps/gn-components.js @@ -36,6 +36,7 @@ import resourceservice from '@js/reducers/resourceservice'; import notifications from '@mapstore/framework/reducers/notifications'; import '@js/observables/persistence'; +import { gnListenToResourcesPendingExecution } from '@js/epics'; const requires = {}; @@ -73,7 +74,8 @@ document.addEventListener('DOMContentLoaded', function() { const appEpics = cleanEpics({ ...configEpics, ...gnresourceEpics, - ...resourceServiceEpics + ...resourceServiceEpics, + gnListenToResourcesPendingExecution }); storeEpicsNamesToExclude(appEpics); diff --git a/geonode_mapstore_client/client/js/epics/index.js b/geonode_mapstore_client/client/js/epics/index.js index 58f577e641..817fdfaba8 100644 --- a/geonode_mapstore_client/client/js/epics/index.js +++ b/geonode_mapstore_client/client/js/epics/index.js @@ -21,6 +21,11 @@ import { SELECT_NODE, updateNode, ADD_LAYER } from '@mapstore/framework/actions/ import { setSelectedDatasetPermissions, setSelectedLayer, updateLayerDataset, setLayerDataset } from '@js/actions/gnresource'; import { updateMapLayoutEpic as msUpdateMapLayoutEpic } from '@mapstore/framework/epics/maplayout'; import isEmpty from 'lodash/isEmpty'; +import { userSelector } from "@mapstore/framework/selectors/security"; +import { getCurrentProcesses } from "@js/selectors/resourceservice"; +import { extractExecutionsFromResources } from "@js/utils/ResourceServiceUtils"; +import { UPDATE_RESOURCES } from "@mapstore/framework/plugins/ResourcesCatalog/actions/resources"; +import { startAsyncProcess } from "@js/actions/resourceservice"; // We need to include missing epics. The plugins that normally include this epic is not used. @@ -124,8 +129,38 @@ export const gnSetDatasetsPermissions = (actions$, { getState = () => {}} = {}) export const updateMapLayoutEpic = msUpdateMapLayoutEpic; +export const gnListenToResourcesPendingExecution = (actions$, { getState = () => {} } = {}) => + actions$.ofType(UPDATE_RESOURCES) + .switchMap((action) => { + const processes = getCurrentProcesses(getState()); + const username = userSelector(getState())?.info?.preferred_username; + const resourcesToTrack = action.resources; + if (!resourcesToTrack?.length || !username) { + return Rx.Observable.empty(); + } + const executions = extractExecutionsFromResources(resourcesToTrack, username) || []; + if (!executions.length) { + return Rx.Observable.empty(); + } + const processesToStart = executions.map((process) => { + const pk = process?.resource?.pk ?? process?.resource?.id; + const processType = process?.processType; + const statusUrl = process?.output?.status_url; + if (!pk || !processType || !statusUrl) { + return null; + } + const foundProcess = processes.find((p) => p?.resource?.pk === pk && p?.processType === processType); + if (!foundProcess) { + return startAsyncProcess({ ...process }); + } + return null; + }).filter((process) => process); + return Rx.Observable.of(...processesToStart); + }); + export default { gnCheckSelectedDatasetPermissions, updateMapLayoutEpic, - gnSetDatasetsPermissions + gnSetDatasetsPermissions, + gnListenToResourcesPendingExecution }; diff --git a/geonode_mapstore_client/client/js/observables/persistence/index.js b/geonode_mapstore_client/client/js/observables/persistence/index.js index c8a897f5fb..a89df96fd7 100644 --- a/geonode_mapstore_client/client/js/observables/persistence/index.js +++ b/geonode_mapstore_client/client/js/observables/persistence/index.js @@ -80,11 +80,9 @@ const persistence = { config, customFilters }).then(({ resources, ...response }) => { - const filteredResources = resources.filter(resource => resource.resource_type); return { ...response, - total: response.total - (resources.length - filteredResources.length), - resources: filteredResources.map((resource) => parseCatalogResource(resource, monitoredState.user)) + resources: resources.map((resource) => parseCatalogResource(resource, monitoredState.user)) }; }); }); diff --git a/geonode_mapstore_client/client/js/plugins/ExecutionTracker/index.jsx b/geonode_mapstore_client/client/js/plugins/ExecutionTracker/index.jsx index 1678b37ce2..995a7909c7 100644 --- a/geonode_mapstore_client/client/js/plugins/ExecutionTracker/index.jsx +++ b/geonode_mapstore_client/client/js/plugins/ExecutionTracker/index.jsx @@ -11,7 +11,6 @@ import { connect } from 'react-redux'; import { createSelector } from 'reselect'; import { createPlugin } from '@mapstore/framework/utils/PluginsUtils'; import { userSelector } from '@mapstore/framework/selectors/security'; -import { getResources } from '@mapstore/framework/plugins/ResourcesCatalog/selectors/resources'; import { startAsyncProcess } from '@js/actions/resourceservice'; import { extractExecutionsFromResources, ProcessTypes } from '@js/utils/ResourceServiceUtils'; @@ -26,23 +25,25 @@ import Message from '@mapstore/framework/components/I18N/Message'; * Plugin that monitors async executions embedded in resources and * triggers the executions API using the existing resourceservice epics. * - * It reads `resources[*].executions` and, when it finds executions and, + * It reads `resources[*].executions` checks for the executions, if found it * dispatches `startAsyncProcess({ resource, output, processType })` once per execution. * + * @param {Object} user - The user object + * @param {Function} onStartAsyncProcess - The function to start an async process + * @param {Object} resourceData - The resource data (details page) + * @param {Array} processes - The processes to track */ function ExecutionTracker({ - resources, user, onStartAsyncProcess, resourceData, processes }) { - const startedKeys = useRef(new Set()); const redirected = useRef(false); useEffect(() => { const username = user?.info?.preferred_username; - const resourcesToTrack = !isEmpty(resourceData) ? [...resources, resourceData] : resources; + const resourcesToTrack = [resourceData]; if (!resourcesToTrack?.length || !username) { return; } @@ -57,13 +58,12 @@ function ExecutionTracker({ if (!pk || !processType || !statusUrl) { return; } - const key = `${pk}:${processType}:${statusUrl}`; - if (!startedKeys.current.has(key)) { - startedKeys.current.add(key); + const foundProcess = processes.find((p) => p?.resource?.pk === pk && p?.processType === processType); + if (!foundProcess) { onStartAsyncProcess(process); } }); - }, [resources, user, onStartAsyncProcess, resourceData]); + }, [user, onStartAsyncProcess, resourceData, processes]); useEffect(() => { if (redirected.current) { @@ -104,7 +104,7 @@ function ExecutionTracker({ return 'gnviewer.deleting'; } return null; - }, [processes, resourceData]); + }, [resourceData, processes]); return msgId ? (
@@ -118,14 +118,8 @@ function ExecutionTracker({ const ExecutionTrackerPlugin = connect( createSelector( - [ - (state) => getResources(state, { id: 'catalog' }), - userSelector, - getResourceData, - getCurrentProcesses - ], - (resources, user, resourceData, processes) => ({ - resources, + [userSelector, getResourceData, getCurrentProcesses], + (user, resourceData, processes) => ({ user, resourceData, processes diff --git a/geonode_mapstore_client/client/js/utils/ResourceServiceUtils.js b/geonode_mapstore_client/client/js/utils/ResourceServiceUtils.js index 37cbfdc9d1..9f23f897a3 100644 --- a/geonode_mapstore_client/client/js/utils/ResourceServiceUtils.js +++ b/geonode_mapstore_client/client/js/utils/ResourceServiceUtils.js @@ -55,7 +55,7 @@ export const extractExecutionsFromResources = (resources, username) => { status_url: statusUrl, user }) => - ['copy', 'copy_geonode_resource'].includes(funcName) + ['copy', 'copy_geonode_resource', 'delete', ProcessTypes.DELETE_RESOURCE, ProcessTypes.COPY_RESOURCE].includes(funcName) && statusUrl && user && user === username ).map((output) => { return { diff --git a/geonode_mapstore_client/client/js/utils/ResourceUtils.js b/geonode_mapstore_client/client/js/utils/ResourceUtils.js index b2bab53703..c05a4fcd6d 100644 --- a/geonode_mapstore_client/client/js/utils/ResourceUtils.js +++ b/geonode_mapstore_client/client/js/utils/ResourceUtils.js @@ -376,6 +376,15 @@ export const isDocumentExternalSource = (resource) => { }; export const getResourceTypesInfo = () => ({ + 'null': { + icon: { glyph: 'dataset' }, + name: '', + canPreviewed: () => false, + formatEmbedUrl: () => undefined, + formatDetailUrl: () => undefined, + formatMetadataUrl: () => undefined, + formatMetadataDetailUrl: () => undefined + }, [ResourceTypes.DATASET]: { icon: { glyph: 'dataset' }, canPreviewed: (resource) => resourceHasPermission(resource, 'view_resourcebase'), diff --git a/geonode_mapstore_client/client/themes/geonode/less/geonode.less b/geonode_mapstore_client/client/themes/geonode/less/geonode.less index d138442750..5ac0cdecf8 100644 --- a/geonode_mapstore_client/client/themes/geonode/less/geonode.less +++ b/geonode_mapstore_client/client/themes/geonode/less/geonode.less @@ -5,6 +5,7 @@ @import '_action-navbar.less'; @import '_brand-navbar.less'; +@import '_execution-tracker.less'; @import '_footer.less'; @import '_hero.less'; @import '_legend.less'; @@ -28,8 +29,6 @@ @import '~font-awesome/css/font-awesome.min.css'; @import '~ol/ol.css'; -@import '_execution-tracker.less'; - :root { font-size: 16px; } diff --git a/geonode_mapstore_client/static/mapstore/configs/localConfig.json b/geonode_mapstore_client/static/mapstore/configs/localConfig.json index b03dfc286b..06ad4059aa 100644 --- a/geonode_mapstore_client/static/mapstore/configs/localConfig.json +++ b/geonode_mapstore_client/static/mapstore/configs/localConfig.json @@ -3491,12 +3491,6 @@ }, { "name": "Favorites" - }, - { - "name": "ExecutionTracker", - "cfg": { - "containerPosition": "header" - } } ], "viewer": [ From 542474b42d67c7cfb305cb4bae65ccca96382c43 Mon Sep 17 00:00:00 2001 From: Sovas Tiwari Date: Thu, 8 Jan 2026 14:38:57 +0545 Subject: [PATCH 4/4] Clean up ResourceCloningIndicator buttons --- .../js/plugins/ActionNavbar/buttons.jsx | 30 ------------------- 1 file changed, 30 deletions(-) diff --git a/geonode_mapstore_client/client/js/plugins/ActionNavbar/buttons.jsx b/geonode_mapstore_client/client/js/plugins/ActionNavbar/buttons.jsx index cce81eea56..f20391352a 100644 --- a/geonode_mapstore_client/client/js/plugins/ActionNavbar/buttons.jsx +++ b/geonode_mapstore_client/client/js/plugins/ActionNavbar/buttons.jsx @@ -31,9 +31,6 @@ import { exportDataResultsControlEnabledSelector, checkingExportDataEntriesSelec import { currentLocaleSelector } from '@mapstore/framework/selectors/locale'; import { checkExportDataEntries, removeExportDataResult } from '@mapstore/framework/actions/layerdownload'; import ExportDataResultsComponent from '@mapstore/framework/components/data/download/ExportDataResultsComponent'; -import FlexBox from '@mapstore/framework/components/layout/FlexBox'; -import Spinner from '@mapstore/framework/components/layout/Spinner'; -import { getCurrentResourceCopyLoading, getCurrentResourceClonedUrl } from '@js/selectors/resourceservice'; // buttons override to use in ActionNavbar for plugin imported from mapstore @@ -196,30 +193,3 @@ export const AddWidgetActionButton = connect( ); }); - -export const ResourceCloningIndicator = connect( - (state) => ({ - isCopying: getCurrentResourceCopyLoading(state), - clonedResourceUrl: getCurrentResourceClonedUrl(state) - }) -)(({ isCopying, clonedResourceUrl }) => { - const className = 'text-primary ms-text _font-size-sm _strong'; - if (isCopying) { - return ( - - - - - ); - } - - if (clonedResourceUrl) { - return ( - - - - ); - } - - return null; -});