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
7 changes: 6 additions & 1 deletion src/components/directory-content-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export type DirectoryContentDialogProps = {
activeElement?: ElementAttributes;
setActiveElement: Dispatch<SetStateAction<ElementAttributes | undefined>>;
selectedDirectoryElementUuid?: UUID;
selectedDirectoryWritable: boolean;
childrenMetadata: ReturnType<typeof useDirectoryContent>[1];
};

Expand All @@ -65,6 +66,7 @@ function DirectoryContentDialog(
setActiveElement,
broadcastChannel,
selectedDirectoryElementUuid,
selectedDirectoryWritable,
childrenMetadata,
}: Readonly<DirectoryContentDialogProps>,
refApi: ForwardedRef<DirectoryContentDialogApi>
Expand Down Expand Up @@ -176,7 +178,9 @@ function DirectoryContentDialog(
case ElementType.STUDY: {
const url = getStudyUrl(event.data.elementUuid);
if (url) {
window.open(url, '_blank');
if (selectedDirectoryWritable) {
window.open(url, '_blank');
}
} else {
snackError({
messageTxt: intl.formatMessage(
Expand Down Expand Up @@ -233,6 +237,7 @@ function DirectoryContentDialog(
getStudyUrl,
intl,
selectedDirectoryElementUuid,
selectedDirectoryWritable,
setActiveElement,
setOpenDialog,
snackError,
Expand Down
51 changes: 24 additions & 27 deletions src/components/directory-content-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export interface DirectoryContentTableProps
handleRowSelected: () => void;
handleCellClick: (event: CellClickedEvent) => void;
colDef: ColDef[];
selectedDirectoryWritable: boolean;
}

const getRowId = (params: GetRowIdParams<ElementAttributes>) => params.data?.elementUuid;
Expand All @@ -40,28 +41,6 @@ const recomputeOverFlowableCells = ({ api }: AgGridEvent) =>

export const CUSTOM_ROW_CLASS = 'custom-row-class';

const getClickableRowStyle = (cellData: RowClassParams<ElementAttributes>) => {
const style: RowStyle = { fontSize: '1rem' };
if (
cellData.data &&
![
ElementType.CASE,
ElementType.DIAGRAM_CONFIG,
ElementType.LOADFLOW_PARAMETERS,
ElementType.SENSITIVITY_PARAMETERS,
ElementType.SECURITY_ANALYSIS_PARAMETERS,
ElementType.VOLTAGE_INIT_PARAMETERS,
ElementType.SHORT_CIRCUIT_PARAMETERS,
ElementType.SPREADSHEET_CONFIG,
ElementType.SPREADSHEET_CONFIG_COLLECTION,
ElementType.NETWORK_VISUALIZATIONS_PARAMETERS,
].includes(cellData.data.type)
) {
style.cursor = 'pointer';
}
return style;
};

const reorderColumns = (colDef: ColDef[], newFieldOrder: string[] | undefined): ColDef[] => {
const fieldIndexMap = new Map(newFieldOrder?.map((field, index) => [field, index]));
return colDef
Expand All @@ -82,14 +61,32 @@ export function DirectoryContentTable({
handleCellClick,
onGridReady,
colDef,
selectedDirectoryWritable,
}: Readonly<DirectoryContentTableProps>) {
const [columnDefs, setColumnDefs] = useState<ColDef[]>(colDef);

const getCustomRowStyle = useCallback(
(cellData: RowClassParams<ElementAttributes>) => ({
...getClickableRowStyle(cellData),
...getRowStyle?.(cellData),
}),
[getRowStyle]
(cellData: RowClassParams<ElementAttributes>) => {
const style: RowStyle = { fontSize: '1rem' };
const unEditableElements = [
ElementType.CASE,
ElementType.DIAGRAM_CONFIG,
ElementType.SPREADSHEET_CONFIG,
ElementType.SPREADSHEET_CONFIG_COLLECTION,
];
if (cellData.data) {
if (cellData.data.type === ElementType.STUDY && !selectedDirectoryWritable) {
style.cursor = 'not-allowed';
} else if (!unEditableElements.includes(cellData.data.type)) {
style.cursor = 'pointer';
}
}
return {
...style,
...getRowStyle?.(cellData),
};
},
[getRowStyle, selectedDirectoryWritable]
);

const dispatch = useDispatch();
Expand Down
27 changes: 23 additions & 4 deletions src/components/directory-content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
import { AppState } from '../redux/types';
import DirectoryContentDialog, { type DirectoryContentDialogApi } from './directory-content-dialog';
import { AnchorStatesType, defaultAnchorStates } from './menus/anchor-utils';
import { checkPermissionOnDirectory } from './menus/menus-utils';
import { PermissionType } from '../utils/rest-api';

const circularProgressSize = '70px';

Expand Down Expand Up @@ -115,6 +117,17 @@
const [openDirectoryMenu, setOpenDirectoryMenu] = useState(false);
const [openContentMenu, setOpenContentMenu] = useState(false);

/** access write on current directory */
const [directoryWritable, setDirectoryWritable] = useState(false);

useEffect(() => {
if (selectedDirectory !== null) {
checkPermissionOnDirectory(selectedDirectory, PermissionType.WRITE).then((b) => {

Check failure on line 125 in src/components/directory-content.tsx

View workflow job for this annotation

GitHub Actions / build / build

Parameter 'b' implicitly has an 'any' type.
setDirectoryWritable(b);
});
}
}, [selectedDirectory]);

const handleOpenContentMenu = useCallback((event: MouseEvent<HTMLDivElement>) => {
setOpenContentMenu(true);
event.stopPropagation();
Expand Down Expand Up @@ -155,11 +168,11 @@
}
// We check if the context menu was triggered from a row to prevent displaying both the directory and the content context menus
const isRow = !!(event.target as Element).closest(`.${CUSTOM_ROW_CLASS}`);
if (!isRow) {
if (isRow) {
handleOpenContentMenu(event);
} else {
dispatch(setActiveDirectory(selectedDirectory?.elementUuid));
handleOpenDirectoryMenu(event);
} else {
handleOpenContentMenu(event);
}
},
[dispatch, handleOpenContentMenu, handleOpenDirectoryMenu, selectedDirectory?.elementUuid]
Expand Down Expand Up @@ -264,7 +277,11 @@
// creates a visual offset rendering the last elements of a full table inaccessible
rows && rows.length > 0 && (
<Box flexShrink={0} sx={styles.toolBarContainer}>
<ContentToolbar selectedElements={checkedRows} />
<ContentToolbar
selectedElements={checkedRows}
selectedDirectory={selectedDirectory}
selectedDirectoryWritable={directoryWritable}
/>
<Button
variant="contained"
endIcon={<AddIcon />}
Expand Down Expand Up @@ -317,6 +334,7 @@
colDef={getColumnsDefinition(childrenMetadata, intl)}
getRowStyle={getRowStyle}
onGridReady={onGridReady}
selectedDirectoryWritable={directoryWritable}
/>
)
}
Expand Down Expand Up @@ -351,6 +369,7 @@
setActiveElement={setActiveElement}
setOpenDialog={setOpenDialog}
selectedDirectoryElementUuid={selectedDirectory?.elementUuid}
selectedDirectoryWritable={directoryWritable}
childrenMetadata={childrenMetadata}
/>
</>
Expand Down
5 changes: 2 additions & 3 deletions src/components/menus/menus-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

import { ElementAttributes, ElementType } from '@gridsuite/commons-ui';
import { hasPermission } from 'utils/rest-api';
import { ElementAttributes, ElementType, hasElementPermission } from '@gridsuite/commons-ui';

Check failure on line 8 in src/components/menus/menus-utils.ts

View workflow job for this annotation

GitHub Actions / build / build

Module '"@gridsuite/commons-ui"' has no exported member 'hasElementPermission'.

export function checkPermissionOnDirectory(element: ElementAttributes, permission: string) {
const directoryUuid = element.type === ElementType.DIRECTORY ? element.elementUuid : element.parentUuid;
return hasPermission(directoryUuid!, permission);
return hasElementPermission(directoryUuid!, permission);
}
57 changes: 24 additions & 33 deletions src/components/toolbars/content-toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

import { useCallback, useEffect, useMemo, useState } from 'react';
import { useSelector } from 'react-redux';
import { useCallback, useMemo, useState } from 'react';
import { useIntl } from 'react-intl';
import {
Delete as DeleteIcon,
Expand All @@ -16,7 +15,7 @@ import {
TableView as TableViewIcon,
} from '@mui/icons-material';
import { ElementAttributes, ElementType, PARAM_DEVELOPER_MODE, useSnackMessage } from '@gridsuite/commons-ui';
import { deleteElements, moveElementsToDirectory, PermissionType } from '../../utils/rest-api';
import { deleteElements, moveElementsToDirectory } from '../../utils/rest-api';
import DeleteDialog from '../dialogs/delete-dialog';
import CommonToolbar, { CommonToolbarProps } from './common-toolbar';
import { useMultipleDeferredFetch } from '../../utils/custom-hooks';
Expand All @@ -25,33 +24,24 @@ import { useDownloadUtils } from '../utils/downloadUtils';
import ExportCaseDialog from '../dialogs/export-case-dialog';
import * as constants from '../../utils/UIconstants';
import { DialogsId } from '../../utils/UIconstants';
import { AppState } from '../../redux/types';
import CreateSpreadsheetCollectionDialog from '../dialogs/spreadsheet-collection-creation-dialog';
import { checkPermissionOnDirectory } from '../menus/menus-utils';
import { handleDeleteError, handleMoveError } from '../utils/rest-errors';
import { useParameterState } from '../dialogs/use-parameters-dialog';

export type ContentToolbarProps = Omit<CommonToolbarProps, 'items'> & {
selectedElements: ElementAttributes[];
selectedDirectory: ElementAttributes | null;
selectedDirectoryWritable: boolean;
};

export default function ContentToolbar(props: Readonly<ContentToolbarProps>) {
const { selectedElements, ...others } = props;
const { selectedElements, selectedDirectory, selectedDirectoryWritable, ...others } = props;
const { snackError } = useSnackMessage();
const intl = useIntl();
const selectedDirectory = useSelector((state: AppState) => state.selectedDirectory);
const { downloadElements, handleConvertCases, stopCasesExports } = useDownloadUtils();
const [deleteError, setDeleteError] = useState('');
const [openDialog, setOpenDialog] = useState(constants.DialogsId.NONE);
const [directoryWritable, setDirectoryWritable] = useState(false);
const [enableDeveloperMode] = useParameterState(PARAM_DEVELOPER_MODE);
useEffect(() => {
if (selectedDirectory !== null) {
checkPermissionOnDirectory(selectedDirectory, PermissionType.WRITE).then((b) => {
setDirectoryWritable(b);
});
}
}, [selectedDirectory]);

const handleOpenDialog = (DialogId: string) => {
setOpenDialog(DialogId);
Expand Down Expand Up @@ -116,24 +106,25 @@ export default function ContentToolbar(props: Readonly<ContentToolbarProps>) {
if (selectedElements.length) {
if (allowsDelete || allowsMove || allowsDownload || allowsExportCases) {
// actions callable for several element types
if (directoryWritable) {
toolbarItems.push({
tooltipTextId: 'delete',
callback: () => {
handleOpenDialog(DialogsId.DELETE);
if (selectedDirectoryWritable) {
toolbarItems.push(
{
tooltipTextId: 'delete',
callback: () => {
handleOpenDialog(DialogsId.DELETE);
},
icon: <DeleteIcon fontSize="small" />,
disabled: !allowsDelete,
},
icon: <DeleteIcon fontSize="small" />,
disabled: !allowsDelete,
});

toolbarItems.push({
tooltipTextId: 'move',
callback: () => {
handleOpenDialog(DialogsId.MOVE);
},
icon: <DriveFileMoveIcon fontSize="small" />,
disabled: !allowsMove,
});
{
tooltipTextId: 'move',
callback: () => {
handleOpenDialog(DialogsId.MOVE);
},
icon: <DriveFileMoveIcon fontSize="small" />,
disabled: !allowsMove,
}
);
}

if (allowsDownload) {
Expand Down Expand Up @@ -174,7 +165,7 @@ export default function ContentToolbar(props: Readonly<ContentToolbarProps>) {
allowsSpreadsheetCollection,
downloadElements,
selectedElements,
directoryWritable,
selectedDirectoryWritable,
enableDeveloperMode,
]);

Expand Down
15 changes: 2 additions & 13 deletions src/utils/rest-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
fetchEnv,
getRequestParamFromList,
getUserToken,
hasElementPermission,

Check failure on line 16 in src/utils/rest-api.ts

View workflow job for this annotation

GitHub Actions / build / build

Module '"@gridsuite/commons-ui"' has no exported member 'hasElementPermission'.
type GsLang,
type GsTheme,
LAST_SELECTED_DIRECTORY,
Expand All @@ -27,7 +28,6 @@
import { CONTINGENCY_ENDPOINTS } from './constants-endpoints';
import { PrepareContingencyListForBackend } from '../components/dialogs/contingency-list-helper';
import { UsersIdentities } from './user-identities.type';
import { HTTP_OK } from './UIconstants';
import { FilterBasedContingencyList, FilteredIdentifiables, FiltersWithEquipmentTypes } from './contingency-list.type';

const PREFIX_USER_ADMIN_SERVER_QUERIES = `${import.meta.env.VITE_API_GATEWAY}/user-admin`;
Expand Down Expand Up @@ -715,17 +715,6 @@
});
};

export function hasPermission(directoryUuid: UUID, permission: string) {
const url = `${PREFIX_EXPLORE_SERVER_QUERIES}/v1/explore/directories/${directoryUuid}?permission=${permission}`;
console.debug(url);
return backendFetch(url, { method: 'head' })
.then((response) => response.status === HTTP_OK)
.catch(() => {
console.info(`Permission to ${permission} in directory ${directoryUuid} denied`);
return false;
});
}

export function fetchDirectoryPermissions(directoryUuid: UUID): Promise<PermissionDTO[]> {
console.info(`Fetching permissions for directory ${directoryUuid}`);
const url = `${PREFIX_EXPLORE_SERVER_QUERIES}/v1/explore/directories/${directoryUuid}/permissions`;
Expand Down Expand Up @@ -756,5 +745,5 @@

// Function to check if user has MANAGE permission on directory
export function hasManagePermission(directoryUuid: UUID): Promise<boolean> {
return hasPermission(directoryUuid, 'MANAGE');
return hasElementPermission(directoryUuid, 'MANAGE');
}
Loading