TP-48342 | Disabled dropdown for unauthorized user and redirect to slack through modal (#93)

* TP-48342 |  Disabled status dropdown for unauthorized user

* TP-48342 |  Disabled dropdown

* TP-48342 | resolved merge conflicts

* TP-48342 | show tooltip only to unauthorized users

* TP-48342 | tooltip position change

* TP-48342 | tooltip position changes

* TP-48342 | added checks for null participants array

* TP-48342 | resolving pr comments

* TP-48342 | fixed modal start adornment

* TP-48342 |  RESOLVED PR REVIEWS

* TP-48342 | MERGED MASTER
This commit is contained in:
Pooja Jaiswal
2023-11-23 13:09:10 +05:30
committed by GitHub
parent 2c151608fb
commit 1bfe9cbcc5
4 changed files with 220 additions and 82 deletions

View File

@@ -15,7 +15,20 @@
.audit-log {
padding-right: 20px;
}
.info-icon {
height: 20px;
width: 20px;
margin-left: 4px;
display: flex;
align-items: center;
:hover {
cursor: pointer;
}
}
.alert-icon {
margin-top: 4px;
padding-bottom: 1px;
}
.tab-content-wrapper {
margin-top: 13px;
margin-left: 24px;
@@ -24,6 +37,7 @@
.log-update-text {
margin-top: 16px;
display: flex;
}
.log-update-dropdown {
@@ -63,6 +77,21 @@
border: 1px solid var(--navi-color-gray-bg-secondary);
}
.dropdown-disabled {
margin-top: 8px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: space-between;
padding: 4px 8px;
background-color: var(--navi-color-gray-bg-primary);
border-radius: 4px;
border: 1px solid var(--navi-color-gray-bg-secondary);
}
.open-box-disabled {
border: 1px solid var(--navi-color-gray-bg-secondary);
background: #fff;
}
.open-box {
border: 1px solid var(--navi-color-blue-base);
background: var(--navi-color-gray-bg-primary);

View File

@@ -30,6 +30,7 @@ export const LINK_JIRA_INCIDENT = `${window?.config?.BASE_API_URL}/houston/link-
export const UNLINK_JIRA_INCIDENT = `${window?.config?.BASE_API_URL}/houston/unlink-jira-from-incident`;
export const SLACK_BASE_URL = 'https://go-navi.slack.com/';
export const FETCH_AUDIT_LOG = (incidentId: string): string => {
return `${window?.config?.BASE_API_URL}/houston/logs/incident/${incidentId}`;
};

View File

@@ -1,11 +1,12 @@
import { FC, useEffect, useState, useReducer, MutableRefObject } from 'react';
import { useMatch } from 'react-router-dom';
import classnames from 'classnames';
import { toast } from '@navi/web-ui/lib/primitives/Toast';
import { ArrowDownIcon } from '@navi/web-ui/lib/icons';
import { AlertOutlineIcon, ArrowDownIcon } from '@navi/web-ui/lib/icons';
import TabItem from '@navi/web-ui/lib-esm/components/Tabs/TabItem';
import Tabs from '@navi/web-ui/lib-esm/components/Tabs/Tabs';
import { SelectPicker } from '@navi/web-ui/lib/components';
import { ModalDialog, Typography } from '@navi/web-ui/lib/primitives';
import { ModalDialog, Tooltip, Typography } from '@navi/web-ui/lib/primitives';
import { SelectPickerOptionProps } from '@navi/web-ui/lib/components/SelectPicker/types';
import GoToLinkIcon from '@src/assets/GoToLinkIcon';
import { ApiService } from '@src/services/api';
@@ -30,6 +31,7 @@ import {
SeverityType,
StatusType,
TeamType,
SLACK_BASE_URL,
} from './constants';
import styles from './Incidents.module.scss';
@@ -39,7 +41,9 @@ const Incident: FC = () => {
const updatedSeverities = generateOptions(state.headerData?.severities);
const updatedStatuses = generateOptions(state.headerData?.incidentStatuses);
const updatedTeams = generateOptions(state.headerData?.teams);
const [open, setOpen] = useState(false);
const userData = JSON.parse(localStorage.getItem('user-data') || '{}');
const userEmail = userData?.emailId || [];
const IncidentMatch = useMatch({
end: true,
path: '/incident/:incidentId',
@@ -61,7 +65,6 @@ const Incident: FC = () => {
map[Incidentteam.value] = Incidentteam.label;
return map;
}, {});
const handleSevClickOutside = () => {
dispatch({
type: actionTypes.SET_IS_SEVERITY_PICKER_OPEN,
@@ -82,7 +85,6 @@ const Incident: FC = () => {
payload: false,
});
};
const refStatus = useOutsideClick({
callback: handleStatusClickOutside,
}) as MutableRefObject<HTMLDivElement>;
@@ -231,6 +233,15 @@ const Incident: FC = () => {
});
};
const participants = state.incidentParticipants?.participants;
const others = state.incidentParticipants?.others;
const participantsList = (participants || []).concat(others || []);
// check if user email id lies in participantsList array
const isUserParticipantList = participantsList?.some(
(participant: any) => participant.email === userEmail,
);
const updateIncident = (payload: any): void => {
const endPoint = UPDATE_INCIDENT;
ApiService.post(endPoint, payload)
@@ -269,7 +280,59 @@ const Incident: FC = () => {
payload: !state.isTeamPickerOpen,
});
};
const combinedSevClassNames = classnames(
styles[isUserParticipantList ? 'dropdown-box' : 'dropdown-disabled'],
{
[styles['open-box']]: state.isSeverityPickerOpen,
[styles['open-box-disabled']]:
state.isSeverityPickerOpen && !isUserParticipantList,
},
);
const combinedStatusClassNames = classnames(
styles[isUserParticipantList ? 'dropdown-box' : 'dropdown-disabled'],
{
[styles['open-box']]: state.isStatusPickerOpen,
[styles['open-box-disabled']]:
state.isStatusPickerOpen && !isUserParticipantList,
},
);
const combinedTeamClassNames = classnames(
styles[isUserParticipantList ? 'dropdown-box' : 'dropdown-disabled'],
{
[styles['open-box']]: state.isTeamPickerOpen,
[styles['open-box-disabled']]:
state.isTeamPickerOpen && !isUserParticipantList,
},
);
const handleDisabledDropdownClick = (): void => {
setOpen(true);
};
const closeModal = (): void => {
setOpen(false);
};
const handleSevClick = () => {
if (isUserParticipantList) {
handleSeverityDropdownClick();
} else {
handleDisabledDropdownClick();
}
};
const handleStatusClick = () => {
if (isUserParticipantList) {
handleStatusDropdownClick();
} else {
handleDisabledDropdownClick();
}
};
const handleTeamClick = () => {
if (isUserParticipantList) {
handleTeamDropdownClick();
} else {
handleDisabledDropdownClick();
}
};
const handleSeveritySelectionChange = (
selectedOption: SelectPickerOptionProps | SelectPickerOptionProps[],
): void => {
@@ -400,8 +463,7 @@ const Incident: FC = () => {
};
const handleGoToSlackChannel = () => {
console.log('hello');
const slackChannelURL = `https://go-navi.slack.com/archives/${state.incidentData?.slackChannel}`;
const slackChannelURL = `${SLACK_BASE_URL}/archives/${state.incidentData?.slackChannel}`;
window.open(slackChannelURL, '_blank');
dispatch({
type: actionTypes.SET_OPEN_DIALOG,
@@ -462,6 +524,19 @@ const Incident: FC = () => {
>
UPDATE INCIDENT
</Typography>
{!isUserParticipantList && (
<div className={styles['info-icon']}>
<Tooltip
text="Only Slack channel participants can update this incident."
withPointer
position={'right'}
>
<div className={styles['alert-icon']}>
<AlertOutlineIcon />
</div>
</Tooltip>
</div>
)}
</div>
<div className={styles['log-update-dropdown']}>
@@ -476,17 +551,17 @@ const Incident: FC = () => {
</div>
<div ref={refSeverity}>
<div
className={`${styles['dropdown-box']} ${
state.isSeverityPickerOpen
? styles['open-box']
: ''
}`}
onClick={handleSeverityDropdownClick}
className={combinedSevClassNames}
onClick={handleSevClick}
>
<div>
<Typography
variant="p4"
color="var(--navi-color-gray-c1)"
color={
isUserParticipantList
? 'var(--navi-color-gray-c1)'
: 'var(--navi-color-gray-c3)'
}
>
{severityMap && state.severity?.value
? severityMap[state.severity.value]
@@ -531,15 +606,17 @@ const Incident: FC = () => {
</div>
<div ref={refStatus}>
<div
className={`${styles['dropdown-box']} ${
state.isStatusPickerOpen ? styles['open-box'] : ''
}`}
onClick={handleStatusDropdownClick}
className={combinedStatusClassNames}
onClick={handleStatusClick}
>
<div>
<Typography
variant="p4"
color="var(--navi-color-gray-c1)"
color={
isUserParticipantList
? 'var(--navi-color-gray-c1)'
: 'var(--navi-color-gray-c3)'
}
>
{incidentStatusMap && state.status?.value
? incidentStatusMap[state.status.value]
@@ -581,15 +658,17 @@ const Incident: FC = () => {
</div>
<div ref={refTeam}>
<div
className={`${styles['dropdown-box']} ${
state.isTeamPickerOpen ? styles['open-box'] : ''
}`}
onClick={handleTeamDropdownClick}
className={combinedTeamClassNames}
onClick={handleTeamClick}
>
<div>
<Typography
variant="p4"
color="var(--navi-color-gray-c1)"
color={
isUserParticipantList
? 'var(--navi-color-gray-c1)'
: 'var(--navi-color-gray-c3)'
}
>
{teamsMap && state.team?.value
? teamsMap[state.team.value]
@@ -603,6 +682,7 @@ const Incident: FC = () => {
/>
</div>
</div>
<div className={styles['team-selectpicker']}>
{state.isTeamPickerOpen && (
<div
@@ -632,6 +712,7 @@ const Incident: FC = () => {
/>
}
</div>
<DescriptionContent
id={state.incidentData?.id}
incidentName={state.incidentData?.incidentName}
@@ -645,7 +726,30 @@ const Incident: FC = () => {
</TabItem>
</Tabs>
</div>
<div>
{open && (
<ModalDialog
open={open}
footerButtons={[
{
label: 'Cancel',
onClick: closeModal,
},
{
label: 'Open Channel',
startAdornment: <GoToLinkIcon color="#fff" />,
onClick: handleGoToSlackChannel,
},
]}
header="You are not authorised to update this incident"
onClose={closeModal}
>
<Typography variant="p4">
You must be a participant of this incident to update it. Please
join the Slack channel to be added a participant
</Typography>
</ModalDialog>
)}
{state.openDialog ? (
<ModalDialog
open={state.openDialog}
footerButtons={[
@@ -661,7 +765,7 @@ const Incident: FC = () => {
{
label: 'Go to slack channel',
onClick: handleGoToSlackChannel,
startAdornment: <GoToLinkIcon />,
startAdornment: <GoToLinkIcon color="#fff" />,
},
]}
header={`${state.dialogText}`}
@@ -674,62 +778,64 @@ const Incident: FC = () => {
mark this incident as {`${state.dialogBodyText}`} on Slack.
</Typography>
</ModalDialog>
</div>
) : null}
<div>
<ModalDialog
open={state.openConfirmationDialog}
footerButtons={[
{
label: 'Cancel',
onClick: () => {
dispatch({
type: actionTypes.SET_OPEN_CONFIRMATION_DIALOG,
payload: false,
});
{state.openConfirmationDialog ? (
<ModalDialog
open={state.openConfirmationDialog}
footerButtons={[
{
label: 'Cancel',
onClick: () => {
dispatch({
type: actionTypes.SET_OPEN_CONFIRMATION_DIALOG,
payload: false,
});
},
},
},
{
label: 'Update incident',
onClick: () => {
dispatch({
type: actionTypes.SET_OPEN_CONFIRMATION_DIALOG,
payload: false,
});
handleCloseConfirmationDialog();
{
label: 'Update incident',
onClick: () => {
dispatch({
type: actionTypes.SET_OPEN_CONFIRMATION_DIALOG,
payload: false,
});
handleCloseConfirmationDialog();
},
},
},
]}
header={`Are you sure you want to update the incident?`}
onClose={() =>
dispatch({
type: actionTypes.SET_OPEN_CONFIRMATION_DIALOG,
payload: false,
})
}
>
<div>
<Typography variant="p3" color="var(--navi-color-gray-c2)">
You are updating this incident&lsquo;s
{getUpdateTypeText(state.updateType)}
from
<Typography
variant="h4"
color="var(--navi-color-gray-c2)"
className={styles['popup-style']}
>
{getUpdateValueText()}
]}
header={`Are you sure you want to update the incident?`}
onClose={() =>
dispatch({
type: actionTypes.SET_OPEN_CONFIRMATION_DIALOG,
payload: false,
})
}
>
<div>
<Typography variant="p3" color="var(--navi-color-gray-c2)">
You are updating this incident&lsquo;s
{getUpdateTypeText(state.updateType)}
from
<Typography
variant="h4"
color="var(--navi-color-gray-c2)"
className={styles['popup-style']}
>
{getUpdateValueText()}
</Typography>
to&nbsp;
<Typography
variant="h4"
color="var(--navi-color-gray-c2)"
className={styles['popup-style']}
>
{state.selectedOption?.label || '..'}
</Typography>
</Typography>
to&nbsp;
<Typography
variant="h4"
color="var(--navi-color-gray-c2)"
className={styles['popup-style']}
>
{state.selectedOption?.label || '..'}
</Typography>
</Typography>
</div>
</ModalDialog>
</div>
</ModalDialog>
) : null}
</div>
</div>
</ErrorBoundary>

View File

@@ -5,6 +5,7 @@ import { IconProps } from './types';
const GoToLinkIcon: FC<IconProps> = ({
width = '16',
height = '16',
color = '#fff',
...restProps
}) => {
return (
@@ -12,8 +13,9 @@ const GoToLinkIcon: FC<IconProps> = ({
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
fill={color}
xmlns="http://www.w3.org/2000/svg"
{...restProps}
>
<g id="Icon">
<mask
@@ -24,13 +26,13 @@ const GoToLinkIcon: FC<IconProps> = ({
width="20"
height="20"
>
<rect id="Bounding box" width="20" height="20" fill="#D9D9D9" />
<rect id="Bounding box" width="20" height="20" fill={color} />
</mask>
<g mask="url(#mask0_1843_6590)">
<path
id="open_in_new"
d="M4.16667 17.5C3.70833 17.5 3.31583 17.3369 2.98917 17.0108C2.66306 16.6842 2.5 16.2917 2.5 15.8333V4.16667C2.5 3.70833 2.66306 3.31583 2.98917 2.98917C3.31583 2.66306 3.70833 2.5 4.16667 2.5H9.16667C9.40278 2.5 9.60083 2.57972 9.76083 2.73917C9.92028 2.89917 10 3.09722 10 3.33333C10 3.56944 9.92028 3.76722 9.76083 3.92667C9.60083 4.08667 9.40278 4.16667 9.16667 4.16667H4.16667V15.8333H15.8333V10.8333C15.8333 10.5972 15.9133 10.3992 16.0733 10.2392C16.2328 10.0797 16.4306 10 16.6667 10C16.9028 10 17.1006 10.0797 17.26 10.2392C17.42 10.3992 17.5 10.5972 17.5 10.8333V15.8333C17.5 16.2917 17.3369 16.6842 17.0108 17.0108C16.6842 17.3369 16.2917 17.5 15.8333 17.5H4.16667ZM7.5 12.5C7.34722 12.3472 7.27083 12.1528 7.27083 11.9167C7.27083 11.6806 7.34722 11.4861 7.5 11.3333L14.6667 4.16667H12.5C12.2639 4.16667 12.0661 4.08667 11.9067 3.92667C11.7467 3.76722 11.6667 3.56944 11.6667 3.33333C11.6667 3.09722 11.7467 2.89917 11.9067 2.73917C12.0661 2.57972 12.2639 2.5 12.5 2.5H16.6667C16.9028 2.5 17.1006 2.57972 17.26 2.73917C17.42 2.89917 17.5 3.09722 17.5 3.33333V7.5C17.5 7.73611 17.42 7.93389 17.26 8.09333C17.1006 8.25333 16.9028 8.33333 16.6667 8.33333C16.4306 8.33333 16.2328 8.25333 16.0733 8.09333C15.9133 7.93389 15.8333 7.73611 15.8333 7.5V5.33333L8.64583 12.5208C8.49306 12.6736 8.30556 12.75 8.08333 12.75C7.86111 12.75 7.66667 12.6667 7.5 12.5Z"
fill="#FCFCFD"
fill={color}
/>
</g>
</g>