TP-24975: added the drawer view for the incident rundown

This commit is contained in:
k.kamalakannan
2023-04-25 12:16:46 +05:30
parent b6bdcb12b8
commit ed9b9e835c
7 changed files with 328 additions and 25 deletions

View File

@@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Houston</title>
<title>Houston UI</title>
</head>
<body>
<div id="root"></div>

View File

@@ -1,10 +1,12 @@
import { FC } from 'react';
import { FC, useState, useRef } from 'react';
import cx from 'classnames';
import { AgTable, Pagination } from '@navi/web-ui/lib/components';
import { Tag } from '@navi/web-ui/lib/primitives';
import { Tag, Drawer } from '@navi/web-ui/lib/primitives';
import { returnFormattedDate } from '@src/services/globalUtils';
import DrawerMode from '@src/Pages/Incidents/DrawerMode/index';
import styles from '../SearchResultsTable.module.scss';
import { useNavigate } from 'react-router-dom';
import { getSeverityColor } from '../constants';
@@ -31,7 +33,8 @@ const SearchResultsTable: FC<SearchResultTableProps> = ({
handlePageSizeChange,
}) => {
const { pageNumber, totalElements } = pageDetails;
const [drawerState, setDrawerState] = useState<boolean>(false);
const incidentData = useRef<any>({});
const navigate = useNavigate();
const rowData = currentPageData?.map((item: any, ind: number) => {
@@ -52,19 +55,16 @@ const SearchResultsTable: FC<SearchResultTableProps> = ({
createdAt: returnFormattedDate(item?.createdAt),
updatedBy: item?.updatedBy,
updatedAt: returnFormattedDate(item?.updatedAt),
slackChannel: item?.slackChannel,
};
});
const columnData = [
{
field: 'sNo',
suppressSizeToFit: true,
width: 90,
suppressMovable: true,
},
{
field: 'id',
headerName: 'Incident Id',
suppressSizeToFit: true,
width: 120,
suppressMovable: true,
},
{
@@ -118,10 +118,23 @@ const SearchResultsTable: FC<SearchResultTableProps> = ({
if (params?.api?.sizeColumnsToFit) params.api.sizeColumnsToFit();
};
const handleRowClick = (event: any) => {
if (event?.data) {
navigate(`/incident/${event.data?.id}`);
}
// const handleRowClick = (event: any) => {
// if (event?.data) {
// navigate(`/incident/${event.data?.id}`);
// }
// };
const handleCloseDrawer = () => {
incidentData.current = {};
setDrawerState(false);
};
const handleOpenDrawer = rowData => {
incidentData.current = {
incidentId: rowData?.data?.id,
slackChannel: rowData?.data?.slackChannel,
};
setDrawerState(true);
};
return (
@@ -152,10 +165,21 @@ const SearchResultsTable: FC<SearchResultTableProps> = ({
suppressCellFocus
defaultColDef={defaultColDef}
onGridSizeChanged={onGridSizeChanged}
onRowClicked={handleRowClick}
onRowClicked={handleOpenDrawer}
suppressColumnMoveAnimation
detailRowAutoHeight={true}
/>
<Drawer
open={drawerState}
onClose={handleCloseDrawer}
className={styles['drawer-wrapper']}
headerText="Incident Rundown"
>
<DrawerMode
incidentId={incidentData?.current?.incidentId}
slackChannel={incidentData?.current?.slackChannel}
/>
</Drawer>
</div>
);
};

View File

@@ -0,0 +1,45 @@
.severity-details-wrapper {
display: flex;
align-items: center;
gap: 0 8px;
margin: 8px 0;
}
.content-info {
display: flex;
align-items: center;
gap: 0 4px;
}
.description-details {
display: flex;
align-items: flex-start;
gap: 0 4px;
margin: 12px 0 16px 0;
.description {
margin-top: -2px;
line-height: 24px;
}
}
.meta-info {
margin: 12px 0 16px 0;
&__title {
margin-bottom: 8px;
}
}
.participant-detail {
display: flex;
align-items: center;
gap: 4px;
}
.team-details-wrapper {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 12px;
}

View File

@@ -0,0 +1,231 @@
import { FC, useEffect, useState } from 'react';
import { Typography, Avatar } from '@navi/web-ui/lib/primitives';
import Filter from '@navi/web-ui/lib/components/Filter';
import { toast } from '@navi/web-ui/lib/primitives/Toast';
import LoadingIcon from '@navi/web-ui/lib/icons/LoadingIcon';
import { ApiService } from '@src/services/api';
import {
FETCH_HEADER_DETAILS,
FETCH_PARTICIPANTS_DATA,
FETCH_INCIDENT_DATA,
UPDATE_INCIDENT,
IncidentConstants,
} from '../constants';
import styles from './DrawerMode.module.scss';
import commonStyles from '../Incidents.module.scss';
interface DrawerModeProps {
incidentId: number;
slackChannel: string;
}
const DrawerMode: FC<DrawerModeProps> = ({ incidentId, slackChannel }) => {
const [severity, setSeverity] = useState<any>();
const [status, setStatus] = useState<any>();
const [team, setTeam] = useState<any>();
const [headerData, setHeaderData] = useState<any>({});
const [incidentDetails, setIncidentDetails] = useState<any>({});
const [incidentParticipants, setIncidentParticipants] = useState<any>([]);
const [isLoading, setIsLoading] = useState<boolean>(false);
const fetchHeaderDetails = (): void => {
const endPoint = FETCH_HEADER_DETAILS;
ApiService.get(endPoint)
.then(response => {
setHeaderData(response?.data?.data);
})
.catch(error => {
const toastMessage = `${
error?.response?.data?.error?.message
? `${error?.response?.data?.error?.message},`
: ''
}`;
toast.error(toastMessage);
setHeaderData({});
});
};
const initFilters = (incidentDetails): void => {
const severity = headerData?.severities?.find(
item => item.value === incidentDetails?.severityId,
);
const status = headerData?.incidentStatuses?.find(
item => item.value === incidentDetails?.status,
);
const team = headerData?.teams?.find(
item => item.value === incidentDetails?.teamId,
);
setSeverity(severity);
setStatus(status);
setTeam(team);
};
const fetchIncidentDetails = (): void => {
const endPoint = FETCH_INCIDENT_DATA(incidentId);
ApiService.get(endPoint)
.then(response => {
setIncidentDetails(response?.data?.data);
initFilters(response?.data?.data);
})
.catch(error => {
const toastMessage = `${
error?.response?.data?.error?.message
? `${error?.response?.data?.error?.message},`
: ''
}`;
toast.error(toastMessage);
setIncidentDetails({});
});
};
const fetchParticipants = (): void => {
const endPoint = FETCH_PARTICIPANTS_DATA(slackChannel);
ApiService.get(endPoint)
.then(response => {
setIncidentParticipants(response?.data?.data);
})
.catch(error => {
const toastMessage = `${
error?.response?.data?.error?.message
? `${error?.response?.data?.error?.message},`
: ''
}`;
toast.error(toastMessage);
setIncidentParticipants([]);
});
};
useEffect(() => {
setIsLoading(true);
if (incidentId && slackChannel) {
fetchIncidentDetails();
fetchHeaderDetails();
fetchParticipants();
}
}, [incidentId, slackChannel]);
useEffect(() => {
if (
Object.keys(incidentDetails).length &&
Object.keys(headerData) &&
incidentParticipants.length
) {
setIsLoading(false);
}
}, [incidentDetails, headerData, incidentParticipants]);
const updateIncident = () => {
const endPoint = UPDATE_INCIDENT;
ApiService.post(endPoint, {
id: incidentId,
teamId: team?.value,
status: status?.value,
severityId: severity?.value,
})
.then(response => {
toast.success('Incident Updated Successfully');
})
.catch(error => {
const toastMessage = `${
error?.response?.data?.error?.message
? `${error?.response?.data?.error?.message},`
: ''
}`;
toast.error(toastMessage);
});
};
const returnHeaderDetails = () => {
return (
<div>
<Typography variant="h4">Update Tags: </Typography>
<div className={styles['severity-details-wrapper']}>
<Typography variant="h5">Severity: </Typography>
<Filter
title={severity?.label ? severity?.label : 'Severity'}
options={headerData?.severities}
onSelectionChange={val => {
setSeverity(val);
updateIncident();
}}
isSingleSelect
filterClass={styles['filter-wrapper']}
/>
</div>
<div className={styles['severity-details-wrapper']}>
<Typography variant="h5">Status: </Typography>
<Filter
title={status?.label ? status?.label : 'Status'}
options={headerData?.incidentStatuses}
onSelectionChange={val => {
setStatus(val);
updateIncident();
}}
isSingleSelect
filterClass={styles['filter-wrapper']}
/>
</div>
<div className={styles['severity-details-wrapper']}>
<Typography variant="h5">Team: </Typography>
<Filter
title={team?.label ? team?.label : 'Team'}
options={headerData?.teams}
onSelectionChange={val => {
setTeam(val);
updateIncident();
}}
isSingleSelect
filterClass={styles['filter-wrapper']}
/>
</div>
</div>
);
};
const returnContent = () => {
return (
<div>
<div className={styles['content-info']}>
<Typography variant="h4">{IncidentConstants.title}:</Typography>
<Typography variant="p3">{incidentDetails?.title}</Typography>
</div>
<div className={styles['description-details']}>
<Typography variant="h4">{IncidentConstants.description}:</Typography>
<Typography variant="p3" className={styles['description']}>
{incidentDetails?.description}
</Typography>
</div>
</div>
);
};
const returnParticipants = () => {
return incidentParticipants?.map(participant => (
<div key={participant?.id} className={styles['team-details-wrapper']}>
<div className={styles['participant-detail']}>
<Avatar size={20} isImage src={participant?.image} />{' '}
<Typography variant="p3">{participant?.name}</Typography>
</div>
</div>
));
};
if (isLoading) {
return <LoadingIcon size="md" />;
}
return (
<div>
{returnContent()}
<hr className={commonStyles['divider']} />
{returnHeaderDetails()}
<hr className={commonStyles['divider']} />
<Typography variant="h4">Participants:</Typography>
{returnParticipants()}
</div>
);
};
export default DrawerMode;

View File

@@ -11,12 +11,12 @@ export const IncidentConstants = {
team: 'Team',
};
export const FETCH_SINGLE_INCIDENT_DATA = (payload: any): string => {
export const FETCH_INCIDENT_DATA = (payload: any): string => {
return `${window?.config?.BASE_API_URL}/incidents/${payload}`;
};
export const FETCH_PARTICIPANTS_DATA = (payload: string): string => {
return `${window?.config?.BASE_API_URL}/users?channelId=${payload}`;
return `${window?.config?.BASE_API_URL}/users?channel_id=${payload}`;
};
export const FETCH_HEADER_DETAILS = `${window?.config?.BASE_API_URL}/incidents/header`;

View File

@@ -8,7 +8,7 @@ import Content from './Content';
import Header from './Header';
import MetaInfo from './MetaInfo';
import { FETCH_SINGLE_INCIDENT_DATA, IncidentConstants } from './constants';
import { FETCH_INCIDENT_DATA, IncidentConstants } from './constants';
import { ApiService } from '@src/services/api';
import styles from './Incidents.module.scss';
@@ -24,7 +24,7 @@ const Incident: FC = () => {
const incidentId = IncidentMatch?.params?.incidentId || '';
const startIncidentSearch = (): void => {
const endPoint = FETCH_SINGLE_INCIDENT_DATA(incidentId);
const endPoint = FETCH_INCIDENT_DATA(incidentId);
ApiService.get(endPoint)
.then(response => {
setData(response?.data?.data);

View File

@@ -9,12 +9,15 @@ export class ApiService {
public static getInstance(): ApiService {
if (!ApiService.instance) {
ApiService.instance = new ApiService();
// TODO: Uncomment this backend is sending Access-Control-Allow-Headers: x-session-token
// ApiService.instance.service.interceptors.request.use((req: any) => {
// req.headers['X-Session-Token'] =
// localStorage.getItem('react-token') || '';
// return req;
// });
const userData = localStorage.getItem('user-data');
ApiService.instance.service.interceptors.request.use((req: any) => {
req.headers['X-Session-Token'] =
localStorage.getItem('react-token') || '';
req.headers['X-User-Email'] = userData
? JSON.parse(userData)?.emailId
: null;
return req;
});
}
return ApiService.instance;