mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-15 19:35:53 +08:00
feat: [SIG-546]: user with viewer roles can only view saved views (#4663)
* feat: [SIG-543]: Users with VIEWER access can create/edit/delete views for logs and traces * feat: [SIG-543]: remove extra code * feat: [SIG-543]: role changes in the save views toolbar * feat: [SIG-543]: role changes in the save views toolbar * feat: remove the save feature / dashboard / alert feature for viewer roles * feat: remove the save feature / dashboard / alert feature for viewer roles * fix: address review comments
This commit is contained in:
parent
49aba4fb1c
commit
6b87118fc6
@ -1,3 +1,6 @@
|
||||
.hide-update {
|
||||
left: calc(50% - 41px) !important;
|
||||
}
|
||||
.explorer-update {
|
||||
position: fixed;
|
||||
bottom: 16px;
|
||||
@ -23,6 +26,10 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ant-divider {
|
||||
margin: 0;
|
||||
height: 28px;
|
||||
@ -55,6 +62,10 @@
|
||||
|
||||
.view-options,
|
||||
.actions {
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
@ -102,6 +113,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.app-content {
|
||||
|
@ -13,6 +13,7 @@ import {
|
||||
Typography,
|
||||
} from 'antd';
|
||||
import axios from 'axios';
|
||||
import cx from 'classnames';
|
||||
import { getViewDetailsUsingViewKey } from 'components/ExplorerCard/utils';
|
||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||
import { QueryParams } from 'constants/query';
|
||||
@ -31,10 +32,14 @@ import { useNotifications } from 'hooks/useNotifications';
|
||||
import { mapCompositeQueryFromQuery } from 'lib/newQueryBuilder/queryBuilderMappers/mapCompositeQueryFromQuery';
|
||||
import { Check, ConciergeBell, Disc3, Plus, X, XCircle } from 'lucide-react';
|
||||
import { CSSProperties, useCallback, useMemo, useRef, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { Dashboard } from 'types/api/dashboard/getAll';
|
||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
import AppReducer from 'types/reducer/app';
|
||||
import { USER_ROLES } from 'types/roles';
|
||||
|
||||
import {
|
||||
DATASOURCE_VS_ROUTES,
|
||||
@ -43,6 +48,9 @@ import {
|
||||
saveNewViewHandler,
|
||||
} from './utils';
|
||||
|
||||
const allowedRoles = [USER_ROLES.ADMIN, USER_ROLES.AUTHOR, USER_ROLES.EDITOR];
|
||||
|
||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||
function ExplorerOptions({
|
||||
disabled,
|
||||
isLoading,
|
||||
@ -71,6 +79,8 @@ function ExplorerOptions({
|
||||
setIsSaveModalOpen(false);
|
||||
};
|
||||
|
||||
const { role } = useSelector<AppState, AppReducer>((state) => state.app);
|
||||
|
||||
const onCreateAlertsHandler = useCallback(() => {
|
||||
history.push(
|
||||
`${ROUTES.ALERTS_NEW}?${QueryParams.compositeQuery}=${encodeURIComponent(
|
||||
@ -247,10 +257,17 @@ function ExplorerOptions({
|
||||
[isDarkMode],
|
||||
);
|
||||
|
||||
const isEditDeleteSupported = allowedRoles.includes(role as string);
|
||||
|
||||
return (
|
||||
<>
|
||||
{isQueryUpdated && (
|
||||
<div className="explorer-update">
|
||||
<div
|
||||
className={cx(
|
||||
isEditDeleteSupported ? '' : 'hide-update',
|
||||
'explorer-update',
|
||||
)}
|
||||
>
|
||||
<Tooltip title="Clear this view" placement="top">
|
||||
<Button
|
||||
className="action-icon"
|
||||
@ -258,10 +275,13 @@ function ExplorerOptions({
|
||||
icon={<X size={14} />}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Divider type="vertical" />
|
||||
<Divider
|
||||
type="vertical"
|
||||
className={isEditDeleteSupported ? '' : 'hidden'}
|
||||
/>
|
||||
<Tooltip title="Update this view" placement="top">
|
||||
<Button
|
||||
className="action-icon"
|
||||
className={cx('action-icon', isEditDeleteSupported ? ' ' : 'hidden')}
|
||||
disabled={isViewUpdating}
|
||||
onClick={onUpdateQueryHandler}
|
||||
icon={<Disc3 size={14} />}
|
||||
@ -323,15 +343,16 @@ function ExplorerOptions({
|
||||
<Button
|
||||
shape="round"
|
||||
onClick={handleSaveViewModalToggle}
|
||||
className={isEditDeleteSupported ? '' : 'hidden'}
|
||||
disabled={viewsIsLoading || isRefetching}
|
||||
>
|
||||
<Disc3 size={16} /> Save this view
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
<hr className={isEditDeleteSupported ? '' : 'hidden'} />
|
||||
|
||||
<div className="actions">
|
||||
<div className={cx('actions', isEditDeleteSupported ? '' : 'hidden')}>
|
||||
<Tooltip title="Create Alerts">
|
||||
<Button
|
||||
disabled={disabled}
|
||||
|
@ -309,6 +309,7 @@ function DashboardsList(): JSX.Element {
|
||||
/>
|
||||
</Col>
|
||||
|
||||
{createNewDashboard && (
|
||||
<Col
|
||||
span={6}
|
||||
style={{
|
||||
@ -341,17 +342,19 @@ function DashboardsList(): JSX.Element {
|
||||
</NewDashboardButton>
|
||||
</Dropdown>
|
||||
</Col>
|
||||
)}
|
||||
</Row>
|
||||
),
|
||||
[
|
||||
isDashboardListLoading,
|
||||
handleSearch,
|
||||
isFilteringDashboards,
|
||||
searchString,
|
||||
createNewDashboard,
|
||||
getMenuItems,
|
||||
newDashboardState.loading,
|
||||
newDashboardState.error,
|
||||
getText,
|
||||
searchString,
|
||||
],
|
||||
);
|
||||
|
||||
|
@ -8,7 +8,6 @@
|
||||
width: calc(100% - 30px);
|
||||
max-width: 736px;
|
||||
|
||||
|
||||
.title {
|
||||
color: var(--bg-vanilla-100);
|
||||
font-size: var(--font-size-lg);
|
||||
@ -37,7 +36,6 @@
|
||||
padding: 0;
|
||||
border: none;
|
||||
background: var(--bg-ink-500);
|
||||
|
||||
}
|
||||
.column-render {
|
||||
margin: 8px 0 !important;
|
||||
@ -75,8 +73,11 @@
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
.view-details {
|
||||
margin-top: 8px;
|
||||
@ -127,7 +128,6 @@
|
||||
}
|
||||
|
||||
.ant-pagination-item {
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
@ -141,7 +141,6 @@
|
||||
font-weight: var(--font-weight-normal);
|
||||
line-height: 20px; /* 142.857% */
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.ant-pagination-item-active {
|
||||
@ -165,7 +164,7 @@
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--bg-slate-500);
|
||||
background: var(--bg-ink-400);
|
||||
box-shadow: 0px -4px 16px 2px rgba(0, 0, 0, 0.20);
|
||||
box-shadow: 0px -4px 16px 2px rgba(0, 0, 0, 0.2);
|
||||
|
||||
.ant-modal-header {
|
||||
padding: 16px;
|
||||
@ -211,7 +210,6 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.ant-modal-footer {
|
||||
@ -255,7 +253,6 @@
|
||||
.lightMode {
|
||||
.save-view-container {
|
||||
.save-view-content {
|
||||
|
||||
.title {
|
||||
color: var(--bg-ink-500);
|
||||
}
|
||||
|
@ -32,14 +32,20 @@ import {
|
||||
} from 'lucide-react';
|
||||
import { ChangeEvent, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { ICompositeMetricQuery } from 'types/api/alerts/compositeQuery';
|
||||
import { ViewProps } from 'types/api/saveViews/types';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
import AppReducer from 'types/reducer/app';
|
||||
import { USER_ROLES } from 'types/roles';
|
||||
|
||||
import { ROUTES_VS_SOURCEPAGE, SOURCEPAGE_VS_ROUTES } from './constants';
|
||||
import { deleteViewHandler } from './utils';
|
||||
|
||||
const allowedRoles = [USER_ROLES.ADMIN, USER_ROLES.AUTHOR, USER_ROLES.EDITOR];
|
||||
|
||||
function SaveView(): JSX.Element {
|
||||
const { pathname } = useLocation();
|
||||
const sourcepage = ROUTES_VS_SOURCEPAGE[pathname];
|
||||
@ -61,6 +67,8 @@ function SaveView(): JSX.Element {
|
||||
setIsDeleteModalOpen(false);
|
||||
};
|
||||
|
||||
const { role } = useSelector<AppState, AppReducer>((state) => state.app);
|
||||
|
||||
const handleDeleteModelOpen = (uuid: string, name: string): void => {
|
||||
setActiveViewKey(uuid);
|
||||
setActiveViewName(name);
|
||||
@ -217,6 +225,9 @@ function SaveView(): JSX.Element {
|
||||
|
||||
// Combine time and date
|
||||
const formattedDateAndTime = `${formattedTime} ⎯ ${formattedDate}`;
|
||||
|
||||
const isEditDeleteSupported = allowedRoles.includes(role as string);
|
||||
|
||||
return (
|
||||
<div className="column-render">
|
||||
<div className="title-with-action">
|
||||
@ -234,11 +245,13 @@ function SaveView(): JSX.Element {
|
||||
<div className="action-btn">
|
||||
<PenLine
|
||||
size={14}
|
||||
className={isEditDeleteSupported ? '' : 'hidden'}
|
||||
onClick={(): void => handleEditModelOpen(view, bgColor)}
|
||||
/>
|
||||
<Compass size={14} onClick={(): void => handleRedirectQuery(view)} />
|
||||
<Trash2
|
||||
size={14}
|
||||
className={isEditDeleteSupported ? '' : 'hidden'}
|
||||
color={Color.BG_CHERRY_500}
|
||||
onClick={(): void => handleDeleteModelOpen(view.uuid, view.name)}
|
||||
/>
|
||||
|
Loading…
x
Reference in New Issue
Block a user