Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"dependencies": {
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
"@gridsuite/commons-ui": "0.234.0",
"@gridsuite/commons-ui": "0.237.0",
"@hello-pangea/dnd": "^18.0.1",
"@hookform/resolvers": "^4.1.3",
"@mui/icons-material": "^6.5.0",
Expand Down
21 changes: 12 additions & 9 deletions src/components/directory-content-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,11 @@ function DirectoryContentDialog(
const itemSelectionForCopy = useSelector((state: AppState) => state.itemSelectionForCopy);
const activeDirectory = useSelector((state: AppState) => state.activeDirectory);
const isDeveloperMode = useSelector((state: AppState) => state.isDeveloperMode);
const user = useSelector((state: AppState) => state.user);

const userProfile = useSelector(
(state: AppState) => state.user?.profile ?? null,
(a, b) =>
a === b || (a?.sub === b?.sub && a?.name === b?.name && a?.email === b?.email && a?.profile === b?.profile)
);
const [languageLocal] = useParameterState(PARAM_LANGUAGE);
const [elementName, setElementName] = useState('');
const [elementDescription, setElementDescription] = useState('');
Expand Down Expand Up @@ -403,7 +406,7 @@ function DirectoryContentDialog(
titleId="editParameters"
name={elementName}
description={activeElement.description}
user={user}
userProfile={userProfile}
Comment thread
ghazwarhili marked this conversation as resolved.
activeDirectory={activeDirectory}
language={languageLocal}
isDeveloperMode={isDeveloperMode}
Expand All @@ -419,7 +422,7 @@ function DirectoryContentDialog(
titleId="editParameters"
name={elementName}
description={activeElement.description}
user={user}
userProfile={userProfile}
activeDirectory={activeDirectory}
language={languageLocal}
/>
Expand All @@ -434,7 +437,7 @@ function DirectoryContentDialog(
titleId="editParameters"
name={elementName}
description={activeElement.description}
user={user}
userProfile={userProfile}
activeDirectory={activeDirectory}
language={languageLocal}
/>
Expand All @@ -449,7 +452,7 @@ function DirectoryContentDialog(
titleId="editParameters"
name={elementName}
description={activeElement.description}
user={user}
userProfile={userProfile}
activeDirectory={activeDirectory}
language={languageLocal}
/>
Expand All @@ -464,7 +467,7 @@ function DirectoryContentDialog(
titleId="editParameters"
name={elementName}
description={activeElement.description}
user={user}
userProfile={userProfile}
activeDirectory={activeDirectory}
language={languageLocal}
/>
Expand All @@ -479,7 +482,7 @@ function DirectoryContentDialog(
titleId="editParameters"
name={elementName}
description={activeElement.description}
user={user}
userProfile={userProfile}
activeDirectory={activeDirectory}
language={languageLocal}
isDeveloperMode={isDeveloperMode}
Expand All @@ -495,7 +498,7 @@ function DirectoryContentDialog(
titleId="editParameters"
name={elementName}
description={activeElement.description}
user={user}
userProfile={userProfile}
activeDirectory={activeDirectory}
language={languageLocal}
/>
Expand Down
42 changes: 42 additions & 0 deletions src/components/silent-renew.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* Copyright (c) 2026, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
import { useCallback, useEffect, useState } from 'react';
import type { UserManager } from 'oidc-client-ts';
import {
handleSilentRenewCallback,
initializeAuthenticationProd,
SilentRenewCallbackHandler,
} from '@gridsuite/commons-ui';
import { getCachedIdpSettings } from '../utils/rest-api';

export default function SilentRenew() {
const [userManager, setUserManager] = useState<UserManager | null>(null);

useEffect(() => {
initializeAuthenticationProd(
() => undefined /* dispatch: unused when isSilentRenew=true */,
true /* isSilentRenew */,
getCachedIdpSettings /* reads the cache instead of fetching idpSettings.json */,
false /* isSigningCallback */
)
.then(setUserManager)
.catch((e) => console.error('Silent renew init failed:', e));
}, []);

const handleSilentRenewCallbackClosure = useCallback(() => {
if (userManager) {
handleSilentRenewCallback(userManager);
}
}, [userManager]);

return (
<SilentRenewCallbackHandler
userManager={userManager}
handleSilentRenewCallback={handleSilentRenewCallbackClosure}
/>
);
}
10 changes: 7 additions & 3 deletions src/components/tree-views-container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,11 @@ export default function TreeViewsContainer({ sourceItemUuid }: { readonly source

const [openDialog, setOpenDialog] = useState(constants.DialogsId.NONE);

const user = useSelector((state: AppState) => state.user);
const userProfile = useSelector(
(state: AppState) => state.user?.profile ?? null,
(a, b) =>
a === b || (a?.sub === b?.sub && a?.name === b?.name && a?.email === b?.email && a?.profile === b?.profile)
);
const selectedDirectory = useSelector((state: AppState) => state.selectedDirectory);
const activeDirectory = useSelector((state: AppState) => state.activeDirectory);
const currentPath = useSelector((state: AppState) => state.currentPath);
Expand Down Expand Up @@ -210,10 +214,10 @@ export default function TreeViewsContainer({ sourceItemUuid }: { readonly source

/* rootDirectories initialization */
useEffect(() => {
if (user != null) {
if (userProfile != null) {
updateRootDirectories();
}
}, [user, updateRootDirectories]);
}, [userProfile, updateRootDirectories]);

/* Manage current path data */
useEffect(() => {
Expand Down
25 changes: 17 additions & 8 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,23 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
import 'core-js/es/array/flat-map';

import 'typeface-roboto';
import './index.css';
import './configure-yup-init';

import { createRoot } from 'react-dom/client';
import AppWrapper from './components/app-wrapper';
import SilentRenew from './components/silent-renew';
import { SILENT_RENEW_CALLBACK_PATH } from './utils/rest-api';

const container = document.getElementById('root');
createRoot(container!).render(<AppWrapper />);
const root = createRoot(container!);

async function renderApp() {
if (globalThis.location.pathname.endsWith(SILENT_RENEW_CALLBACK_PATH)) {
root.render(<SilentRenew />);
return;
}
await import('core-js/es/array/flat-map');

Check warning on line 19 in src/index.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

All polyfilled features imported from `core-js/es/array/flat-map` are available as built-ins. Use the built-ins instead.

See more on https://sonarcloud.io/project/issues?id=gridsuite_gridexplore-app&issues=AZ7-DCLmIk0o4u-Mhp4R&open=AZ7-DCLmIk0o4u-Mhp4R&pullRequest=889
await import('typeface-roboto');
await import('./index.css');
await import('./configure-yup-init');
const { default: AppWrapper } = await import('./components/app-wrapper');
root.render(<AppWrapper />);
}
renderApp().catch((error) => console.error(error));

Check warning on line 26 in src/index.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer top-level await over using a promise chain.

See more on https://sonarcloud.io/project/issues?id=gridsuite_gridexplore-app&issues=AZ7-DCLmIk0o4u-Mhp4S&open=AZ7-DCLmIk0o4u-Mhp4S&pullRequest=889
7 changes: 7 additions & 0 deletions src/module-core-js-array-flat-map.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* Copyright (c) 2026, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
declare module 'core-js/es/array/flat-map';
7 changes: 7 additions & 0 deletions src/module-typeface-roboto.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* Copyright (c) 2026, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
declare module 'typeface-roboto';
33 changes: 31 additions & 2 deletions src/utils/rest-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
type GsLang,
type GsTheme,
hasElementPermission,
IdpSettings,
LAST_SELECTED_DIRECTORY,
PARAM_DEVELOPER_MODE,
PARAM_LANGUAGE,
Expand Down Expand Up @@ -47,6 +48,8 @@ const PREFIX_FILTERS_QUERIES = `${import.meta.env.VITE_API_GATEWAY}/filter/v1/fi
const PREFIX_STUDY_QUERIES = `${import.meta.env.VITE_API_GATEWAY}/study`;
const PREFIX_SPREADSHEET_CONFIG_QUERIES = `${import.meta.env.VITE_API_GATEWAY}/study-config`;
const PREFIX_MONITOR_QUERIES = `${import.meta.env.VITE_API_GATEWAY}/monitor`;
const IDP_SETTINGS_CACHE_KEY = 'gridsuite-idp-settings';
export const SILENT_RENEW_CALLBACK_PATH = '/silent-renew-callback';

export type KeyOfWithoutIndexSignature<T> = {
// copy every declared property from T but remove index signatures
Expand Down Expand Up @@ -96,8 +99,34 @@ const getContingencyUriParamType = (contingencyListType: string | null | undefin
}
};

export function fetchIdpSettings() {
return fetch('idpSettings.json').then((res) => res.json());
// Always hits the network: picks up config changes on each full app load
// AND refreshes the cache read by the silent-renew iframe.
export function fetchIdpSettings(): Promise<IdpSettings> {
return fetch('idpSettings.json')
.then((res) => res.json())
.then((settings: IdpSettings) => {
try {
localStorage.setItem(IDP_SETTINGS_CACHE_KEY, JSON.stringify(settings));
} catch (e) {
console.warn('Failed to cache IdP settings:', e);
}
return settings;
});
}

// Used only on the silent-renew path: reads the cache (no network),
// falls back to a real fetch if the cache is missing/corrupted.
export function getCachedIdpSettings(): Promise<IdpSettings> {
try {
const cached = localStorage.getItem(IDP_SETTINGS_CACHE_KEY);
if (cached) {
return Promise.resolve(JSON.parse(cached) as IdpSettings);
}
} catch (e) {
// localStorage unavailable, or cache corrupted -> fall back to fresh fetch
console.warn('Failed to read cached IdP settings:', e);
}
return fetchIdpSettings();
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

export function fetchVersion() {
Expand Down
Loading