INFRA-2867 | add modal for product change
This commit is contained in:
@@ -72,7 +72,7 @@
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
background-color: white;
|
||||
border-radius: 50%; /* Makes the div round */
|
||||
border-radius: 50%;
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,6 +147,7 @@
|
||||
|
||||
.create-incident-product-selector {
|
||||
width: 335px !important;
|
||||
min-height: unset;
|
||||
> div > div {
|
||||
span {
|
||||
color: var(--navi-color-blue-base) !important;
|
||||
@@ -155,4 +156,5 @@
|
||||
}
|
||||
.create-incident-single-selector {
|
||||
width: 321px !important;
|
||||
min-height: unset;
|
||||
}
|
||||
|
||||
@@ -2,15 +2,16 @@ import { FC } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { AgTable, Pagination } from '@navi/web-ui/lib/components';
|
||||
import { Tag } from '@navi/web-ui/lib/primitives';
|
||||
import { Tooltip } from 'antd';
|
||||
import cx from 'classnames';
|
||||
import { returnFormattedDate } from '@src/services/globalUtils';
|
||||
import useClickStream from '@src/services/clickStream';
|
||||
import { CLICK_STREAM_EVENT_FACTORY } from '@src/services/clickStream/constants/values';
|
||||
import styles from '../SearchResultsTable.module.scss';
|
||||
import { getSeverityColor } from '../constants';
|
||||
import cx from 'classnames';
|
||||
import { getSeverityColor } from '@src/Pages/Dashboard/constants';
|
||||
import { IncidentDashboard } from '@src/slices/dashboardSlice';
|
||||
import { Team } from '../type';
|
||||
import { Tooltip } from 'antd';
|
||||
import { Team } from '@src/Pages/Dashboard/type';
|
||||
import styles from '@src/Pages/Dashboard/partials/CreateIncident.module.scss';
|
||||
|
||||
interface SearchResultTableProps {
|
||||
currentPageData: IncidentDashboard[];
|
||||
pageDetails: any;
|
||||
@@ -76,9 +77,7 @@ const SearchResultsTable: FC<SearchResultTableProps> = ({
|
||||
const wrappedText = param?.substring(0, MAX_LENGTH - 3) + '...';
|
||||
return (
|
||||
<div className={styles.tooltipWrapper}>
|
||||
<Tooltip title={param}>
|
||||
<div className={styles.tooltip}>{wrappedText}</div>
|
||||
</Tooltip>
|
||||
<Tooltip title={param}>{wrappedText}</Tooltip>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { FC, useReducer } from 'react';
|
||||
import { FC, useEffect, useReducer } from 'react';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
import { AlertOutlineIcon } from '@navi/web-ui/lib/icons';
|
||||
import { SelectPickerOptionProps } from '@navi/web-ui/lib/components/SelectPicker/types';
|
||||
import { AlertOutlineIcon, InfoIcon } from '@navi/web-ui/lib/icons';
|
||||
import {
|
||||
SelectPickerOptionProps,
|
||||
SelectPickerValue,
|
||||
} from '@navi/web-ui/lib/components/SelectPicker/types';
|
||||
import {
|
||||
BorderedInput,
|
||||
ModalDialog,
|
||||
@@ -29,6 +32,11 @@ import {
|
||||
getSeverityValue,
|
||||
} from '../constants';
|
||||
import useIncidentApis from '../useIncidentApis';
|
||||
import { FETCH_ASSIGNER_AND_RESPONDER } from '@src/Pages/Dashboard/constants';
|
||||
import { ApiService } from '@src/services/api';
|
||||
|
||||
import IncidentSingleSelector from '@src/Pages/Dashboard/partials/IncidentSingleSelector';
|
||||
import { Team } from '@src/Pages/Dashboard/type';
|
||||
import styles from '../Incidents.module.scss';
|
||||
|
||||
const AllDailogBox: FC = () => {
|
||||
@@ -59,6 +67,35 @@ const AllDailogBox: FC = () => {
|
||||
const incidentId = incidentData?.id?.toString() || '';
|
||||
const reduxDispatch = useDispatch();
|
||||
|
||||
useEffect(() => {
|
||||
if (UpdateData?.type == 'product') {
|
||||
const product = UpdateData?.to as SelectPickerOptionProps[];
|
||||
const productIds = product?.map(option => option.value as number);
|
||||
ApiService.get(FETCH_ASSIGNER_AND_RESPONDER(productIds))?.then(
|
||||
response => {
|
||||
const responderTeams = response?.data?.data?.responderTeam?.teams;
|
||||
dispatch({
|
||||
type: actionTypes.SET_RESPONDERS,
|
||||
payload: responderTeams,
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
}, [UpdateData]);
|
||||
|
||||
useEffect(() => {
|
||||
if (state?.responderList) {
|
||||
const responderTeams = state?.responderList?.map(
|
||||
(team: Team) => team.value as number,
|
||||
);
|
||||
if (!responderTeams.includes(incidentData.teamId)) return;
|
||||
dispatch({
|
||||
type: actionTypes.SET_TEAM,
|
||||
payload: incidentData?.teamId,
|
||||
});
|
||||
}
|
||||
}, [incidentData, state.responderList]);
|
||||
|
||||
const updateDetailsFrom = getSeverityValue(UpdateData?.from?.trim() || '');
|
||||
const updateDetailsTo = (
|
||||
UpdateData as { to?: { value: string; label: string } }
|
||||
@@ -205,6 +242,7 @@ const AllDailogBox: FC = () => {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleTeamSelectionChange = (
|
||||
selectedOption: SelectPickerOptionProps | SelectPickerOptionProps[] | null,
|
||||
): void => {
|
||||
@@ -223,15 +261,35 @@ const AllDailogBox: FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleProductSelectionChange = (
|
||||
selectedOption: SelectPickerOptionProps[] | null,
|
||||
): void => {
|
||||
if (selectedOption) {
|
||||
dispatch({
|
||||
type: actionTypes.SET_IS_PRODUCT_PICKER_OPEN,
|
||||
payload: false,
|
||||
});
|
||||
toast('Updating ticket. Please wait a moment.', {
|
||||
icon: <LoadingIcon />,
|
||||
});
|
||||
const value = selectedOption.map(option => option.value as number);
|
||||
updateIncident({
|
||||
id: parseInt(incidentId, 10),
|
||||
productIds: value,
|
||||
teamId: state?.team?.toString(),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleCloseConfirmationDialog = () => {
|
||||
if (UpdateData?.type == 'severity') {
|
||||
handleSeveritySelectionChange(selectedOption);
|
||||
} else {
|
||||
if (UpdateData?.type == 'status') {
|
||||
} else if (UpdateData?.type == 'status') {
|
||||
handleStatusSelectionChange(selectedOption);
|
||||
} else {
|
||||
} else if (UpdateData?.type == 'team') {
|
||||
handleTeamSelectionChange(selectedOption);
|
||||
}
|
||||
} else {
|
||||
handleProductSelectionChange(selectedOption as SelectPickerOptionProps[]);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -239,7 +297,7 @@ const AllDailogBox: FC = () => {
|
||||
option: SelectPickerOptionProps | SelectPickerOptionProps[] | null,
|
||||
): string | undefined => {
|
||||
if (Array.isArray(option)) {
|
||||
return option[0]?.label;
|
||||
return option?.map(opt => opt.label).join(', ');
|
||||
} else {
|
||||
return option?.label;
|
||||
}
|
||||
@@ -358,19 +416,59 @@ const AllDailogBox: FC = () => {
|
||||
</div>
|
||||
);
|
||||
|
||||
const customFooter = (): JSX.Element => (
|
||||
<div className={styles['modal-footer']}>
|
||||
{UpdateData.type === 'severity' &&
|
||||
const customFooter = (): JSX.Element => {
|
||||
const isSeverityAndDisabled =
|
||||
UpdateData.type === 'severity' &&
|
||||
showJustificationBox &&
|
||||
state.reason.length <= 5 ? (
|
||||
state.reason.length <= 5;
|
||||
return (
|
||||
<div className={styles['modal-footer']}>
|
||||
{isSeverityAndDisabled ? (
|
||||
<Tooltip text="Please enter atleast 5 characters" position="left">
|
||||
<Button disabled>Update incident</Button>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Button onClick={handleUpdateIncidentClick}>Update incident</Button>
|
||||
<Button
|
||||
onClick={handleUpdateIncidentClick}
|
||||
disabled={UpdateData.type === 'product' && !state.team}
|
||||
>
|
||||
Update incident
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const renderResponderChange = () => {
|
||||
const handleResponderChange = (val: SelectPickerValue) => {
|
||||
dispatch({ type: actionTypes.SET_TEAM, payload: val });
|
||||
};
|
||||
return (
|
||||
<div className={styles['product-selector']}>
|
||||
<Typography variant="p3" color="var(--navi-color-gray-c2)">
|
||||
Please review the responder for this incident and change if necessary.
|
||||
</Typography>
|
||||
<div className={styles['product-header-wrapper']}>
|
||||
<Typography variant="p4" className={styles['responder']}>
|
||||
{' '}
|
||||
Responder
|
||||
</Typography>
|
||||
<Tooltip
|
||||
position="right"
|
||||
text="If no responder selected, the team was not mapped to the updated product"
|
||||
withPointer
|
||||
>
|
||||
<InfoIcon />
|
||||
</Tooltip>
|
||||
</div>
|
||||
<IncidentSingleSelector
|
||||
options={state.responderList}
|
||||
handleSelectionChange={handleResponderChange}
|
||||
selected={state.team}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const renderUpdateDialog = (): JSX.Element => (
|
||||
<div>
|
||||
@@ -402,7 +500,7 @@ const AllDailogBox: FC = () => {
|
||||
{getLabelFromOption(UpdateData?.to) || '..'}
|
||||
</Typography>
|
||||
</Typography>
|
||||
{UpdateData.type === 'severity' && showJustificationBox ? (
|
||||
{UpdateData.type === 'severity' && showJustificationBox && (
|
||||
<div>
|
||||
<textarea
|
||||
placeholder="Enter reason for de-escalation"
|
||||
@@ -420,7 +518,8 @@ const AllDailogBox: FC = () => {
|
||||
{state.reason.length} / 100 characters
|
||||
</Typography>
|
||||
</div>
|
||||
) : null}
|
||||
)}
|
||||
{UpdateData.type === 'product' && renderResponderChange()}
|
||||
</div>
|
||||
</ModalDialog>
|
||||
) : null}
|
||||
|
||||
31
src/Pages/Incidents/Dropdowns/DropDownHelper.tsx
Normal file
31
src/Pages/Incidents/Dropdowns/DropDownHelper.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import { Typography } from '@navi/web-ui/lib/primitives';
|
||||
import { Team } from '@src/Pages/Dashboard/type';
|
||||
|
||||
import styles from '../Incidents.module.scss';
|
||||
|
||||
export const getProduct = (initialProducts: Team[] | null) => {
|
||||
return initialProducts?.map(product => product?.label).join(', ') || '';
|
||||
};
|
||||
|
||||
export const getTruncatedProduct = (initialProducts: Team[] | null) => {
|
||||
const product = getProduct(initialProducts);
|
||||
const TRUNCATE_PRODUCT_LIMIT = 30;
|
||||
const truncatedProduct =
|
||||
product?.length > TRUNCATE_PRODUCT_LIMIT
|
||||
? `${product?.slice(0, TRUNCATE_PRODUCT_LIMIT)}...`
|
||||
: product;
|
||||
const showMore = product?.length > TRUNCATE_PRODUCT_LIMIT ? true : false;
|
||||
const remaining =
|
||||
initialProducts?.length ?? 0 - truncatedProduct?.split(',')?.length;
|
||||
return (
|
||||
<div className={styles['product-text']}>
|
||||
<Typography variant="p4">{truncatedProduct}</Typography>
|
||||
{showMore && (
|
||||
<Typography variant="p4" color="var(--navi-color-blue-base)">
|
||||
{' '}
|
||||
+ {remaining} more
|
||||
</Typography>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,11 +1,14 @@
|
||||
import { FC, useReducer, MutableRefObject } from 'react';
|
||||
import { FC, useReducer, MutableRefObject, useEffect } from 'react';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
import classnames from 'classnames';
|
||||
import { Typography } from '@navi/web-ui/lib/primitives';
|
||||
import { Tooltip, Typography } from '@navi/web-ui/lib/primitives';
|
||||
import { ArrowDownIcon } from '@navi/web-ui/lib/icons';
|
||||
import { Button, Drawer } from '@navi/web-ui/lib/primitives';
|
||||
import { Drawer } from '@navi/web-ui/lib/primitives';
|
||||
import { SelectPicker } from '@navi/web-ui/lib/components';
|
||||
import { SelectPickerOptionProps } from '@navi/web-ui/lib/components/SelectPicker/types';
|
||||
import {
|
||||
SelectPickerOptionProps,
|
||||
SelectPickerValue,
|
||||
} from '@navi/web-ui/lib/components/SelectPicker/types';
|
||||
import useOutsideClick from '@src/services/hooks/useOustideClick';
|
||||
import { Participant } from '@src/types';
|
||||
import {
|
||||
@@ -36,7 +39,14 @@ import {
|
||||
SeverityType,
|
||||
StatusType,
|
||||
TeamType,
|
||||
ProductType,
|
||||
} from '../constants';
|
||||
import { getProduct, getTruncatedProduct } from './DropDownHelper';
|
||||
import {
|
||||
selectAssignerResponderData,
|
||||
selectProductListData,
|
||||
} from '@src/slices/createIncidentSlice';
|
||||
|
||||
import styles from '../Incidents.module.scss';
|
||||
|
||||
const Dropdowns: FC = () => {
|
||||
@@ -46,6 +56,7 @@ const Dropdowns: FC = () => {
|
||||
const incidentData = useSelector(
|
||||
(state: IncidentPageState) => state.incidentLog.incidentData,
|
||||
);
|
||||
|
||||
const currentIncidentId = incidentData?.id;
|
||||
const slackChannel = incidentData?.slackChannel;
|
||||
const incidentParticipants = useSelector(
|
||||
@@ -54,11 +65,13 @@ const Dropdowns: FC = () => {
|
||||
const headerData = useSelector(
|
||||
(state: IncidentPageState) => state.incidentLog.headerData,
|
||||
);
|
||||
|
||||
const productOptions = useSelector(selectProductListData)?.products;
|
||||
const [state, dispatch] = useReducer(reducer, initialState);
|
||||
|
||||
const updatedSeverities = generateOptions(headerData?.severities);
|
||||
const updatedStatuses = generateOptions(headerData?.incidentStatuses);
|
||||
const updatedTeams = generateOptions(headerData?.teams);
|
||||
const updatedTeams = useSelector(selectAssignerResponderData)?.assignerTeam
|
||||
?.teams;
|
||||
const initialSeverity = {
|
||||
label: incidentData?.severityName,
|
||||
value: incidentData?.severityId,
|
||||
@@ -71,6 +84,15 @@ const Dropdowns: FC = () => {
|
||||
label: incidentData?.teamName,
|
||||
value: incidentData?.teamId,
|
||||
};
|
||||
const initialProducts = incidentData?.products;
|
||||
useEffect(() => {
|
||||
dispatch({
|
||||
type: actionTypes.SET_SELECTED_PRODUCTS,
|
||||
payload: initialProducts,
|
||||
});
|
||||
}, [initialProducts]);
|
||||
|
||||
useEffect(() => {}, [incidentData]);
|
||||
const { emailId: userEmail } =
|
||||
JSON.parse(localStorage.getItem('user-data') || '{}') || {};
|
||||
const participantsList = [
|
||||
@@ -85,34 +107,39 @@ const Dropdowns: FC = () => {
|
||||
const severityMap = generateMap(headerData?.severities || []);
|
||||
const statusMap = generateMap(headerData?.incidentStatuses || []);
|
||||
const teamsMap = generateMap(headerData?.teams || []);
|
||||
const productMap = generateMap(productOptions || []);
|
||||
|
||||
const currentSev = getCurrentData(incidentData, severityMap, 'severityId');
|
||||
const currentStatus = getCurrentData(incidentData, statusMap, 'status');
|
||||
const currentTeam = getCurrentData(incidentData, teamsMap, 'teamId');
|
||||
|
||||
const combinedSevClassNames = classnames(
|
||||
const getCombinedClassNames = (
|
||||
isOpen: boolean,
|
||||
isUserParticipantList: boolean,
|
||||
) =>
|
||||
classnames(
|
||||
styles[isUserParticipantList ? 'dropdown-box' : 'dropdown-disabled'],
|
||||
{
|
||||
[styles['open-box']]: state.isSeverityPickerOpen,
|
||||
[styles['open-box-disabled']]:
|
||||
state.isSeverityPickerOpen && !isUserParticipantList,
|
||||
[styles['open-box']]: isOpen,
|
||||
[styles['open-box-disabled']]: isOpen && !isUserParticipantList,
|
||||
},
|
||||
);
|
||||
const combinedStatusClassNames = classnames(
|
||||
styles[isUserParticipantList ? 'dropdown-box' : 'dropdown-disabled'],
|
||||
{
|
||||
[styles['open-box']]: state.isStatusPickerOpen,
|
||||
[styles['open-box-disabled']]:
|
||||
state.isStatusPickerOpen && !isUserParticipantList,
|
||||
},
|
||||
|
||||
const combinedSevClassNames = getCombinedClassNames(
|
||||
state.isSeverityPickerOpen,
|
||||
isUserParticipantList,
|
||||
);
|
||||
const combinedTeamClassNames = classnames(
|
||||
styles[isUserParticipantList ? 'dropdown-box' : 'dropdown-disabled'],
|
||||
{
|
||||
[styles['open-box']]: state.isTeamPickerOpen,
|
||||
[styles['open-box-disabled']]:
|
||||
state.isTeamPickerOpen && !isUserParticipantList,
|
||||
},
|
||||
const combinedStatusClassNames = getCombinedClassNames(
|
||||
state.isStatusPickerOpen,
|
||||
isUserParticipantList,
|
||||
);
|
||||
const combinedTeamClassNames = getCombinedClassNames(
|
||||
state.isTeamPickerOpen,
|
||||
isUserParticipantList,
|
||||
);
|
||||
const combinedProductClassNames = getCombinedClassNames(
|
||||
state.isProductPickerOpen,
|
||||
isUserParticipantList,
|
||||
);
|
||||
|
||||
const handleDropdownClick = (changeType: string): (() => void) => {
|
||||
@@ -124,6 +151,8 @@ const Dropdowns: FC = () => {
|
||||
return () => handleStatusDropdownClick();
|
||||
case 'team':
|
||||
return () => handleTeamDropdownClick();
|
||||
case 'product':
|
||||
return () => handleProductDropdownClick();
|
||||
default:
|
||||
throw new Error('Invalid change type');
|
||||
}
|
||||
@@ -150,10 +179,38 @@ const Dropdowns: FC = () => {
|
||||
payload: !state.isTeamPickerOpen,
|
||||
});
|
||||
};
|
||||
const handleProductDropdownClick = (): void => {
|
||||
dispatch({
|
||||
type: actionTypes.SET_SELECTED_PRODUCTS,
|
||||
payload: initialProducts,
|
||||
});
|
||||
dispatch({
|
||||
type: actionTypes.SET_IS_PRODUCT_PICKER_OPEN,
|
||||
payload: !state.isProductPickerOpen,
|
||||
});
|
||||
};
|
||||
const handleDisabledDropdownClick = (): void => {
|
||||
reduxDispatch(setOpenDialognotParticipants(true));
|
||||
};
|
||||
|
||||
const handleProductPickerCancelClick = (): void => {
|
||||
dispatch({ type: actionTypes.SET_IS_PRODUCT_PICKER_OPEN, payload: false });
|
||||
dispatch({
|
||||
type: actionTypes.SET_SELECTED_PRODUCTS,
|
||||
payload: initialProducts,
|
||||
});
|
||||
};
|
||||
const handleProductSelectionChange = (
|
||||
options: SelectPickerOptionProps | SelectPickerOptionProps[],
|
||||
) => {
|
||||
dispatch({ type: actionTypes.SET_SELECTED_PRODUCTS, payload: options });
|
||||
};
|
||||
const handleApplyProductSelection = (
|
||||
selectedOption: SelectPickerOptionProps[],
|
||||
) => {
|
||||
dispatch({ type: actionTypes.SET_IS_PRODUCT_PICKER_OPEN, payload: false });
|
||||
handleOpenConfirmationDailog(selectedOption, ProductType);
|
||||
};
|
||||
const handleDispatch = (
|
||||
updateType: number,
|
||||
selectedOption: SelectPickerOptionProps | SelectPickerOptionProps[],
|
||||
@@ -164,8 +221,13 @@ const Dropdowns: FC = () => {
|
||||
to: selectedOption,
|
||||
from: getUpdateValueText({
|
||||
updateType: updateType,
|
||||
maps: { severityMap, statusMap, teamsMap },
|
||||
initialValues: { initialSeverity, initialStatus, initialTeam },
|
||||
maps: { severityMap, statusMap, teamsMap, productMap },
|
||||
initialValues: {
|
||||
initialSeverity,
|
||||
initialStatus,
|
||||
initialTeam,
|
||||
initialProducts,
|
||||
},
|
||||
}),
|
||||
}),
|
||||
);
|
||||
@@ -173,25 +235,37 @@ const Dropdowns: FC = () => {
|
||||
reduxDispatch(setOpenDialogUpdate(true));
|
||||
};
|
||||
|
||||
const getCurrentValue = (updateType: number) => {
|
||||
switch (updateType) {
|
||||
case SeverityType:
|
||||
return initialSeverity?.value;
|
||||
case StatusType:
|
||||
return initialStatus?.value;
|
||||
case TeamType:
|
||||
return initialTeam?.value;
|
||||
case ProductType:
|
||||
return initialProducts?.map(
|
||||
product => product?.value as SelectPickerValue,
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const handleOpenConfirmationDailog = (
|
||||
selectedOption: SelectPickerOptionProps | SelectPickerOptionProps[],
|
||||
updateType: number,
|
||||
): void => {
|
||||
const currentValue =
|
||||
updateType === SeverityType
|
||||
? initialSeverity?.value
|
||||
: updateType === StatusType
|
||||
? initialStatus?.value
|
||||
: initialTeam?.value;
|
||||
const currentState = currentValue?.toString();
|
||||
const selectedvalue = Array.isArray(selectedOption)
|
||||
? selectedOption[0].value
|
||||
const currentValue = getCurrentValue(updateType);
|
||||
const selectedValue = Array.isArray(selectedOption)
|
||||
? selectedOption?.map(option => option.value)
|
||||
: selectedOption.value;
|
||||
if (currentState !== selectedvalue) {
|
||||
|
||||
if (currentValue != selectedValue) {
|
||||
fireEvent(EVENT_NAME.Houston_Resolve_Incident_initiate, {
|
||||
screen_name: SCREEN_NAME.INCIDENT_PAGE,
|
||||
});
|
||||
if (updateType === StatusType && selectedvalue === RESOLVE_STATUS) {
|
||||
if (updateType === StatusType && selectedValue === RESOLVE_STATUS) {
|
||||
dispatch({
|
||||
type: actionTypes.SET_IS_INCIDENT_RESOLVED,
|
||||
payload: true,
|
||||
@@ -202,13 +276,15 @@ const Dropdowns: FC = () => {
|
||||
});
|
||||
} else if (
|
||||
updateType === StatusType &&
|
||||
selectedvalue === DUPLICATE_STATUS
|
||||
selectedValue === DUPLICATE_STATUS
|
||||
) {
|
||||
dispatch({
|
||||
type: actionTypes.SET_DUPLICATE_DIALOG,
|
||||
payload: true,
|
||||
});
|
||||
reduxDispatch(setOpenDialogDuplicate(true));
|
||||
} else if (updateType === ProductType) {
|
||||
handleDispatch(updateType, selectedOption);
|
||||
} else {
|
||||
handleDispatch(updateType, selectedOption);
|
||||
}
|
||||
@@ -220,35 +296,23 @@ const Dropdowns: FC = () => {
|
||||
payload: false,
|
||||
});
|
||||
};
|
||||
const handleSevClickOutside = (): void => {
|
||||
const handlePickerClickOutside = (pickerType: string) => {
|
||||
dispatch({
|
||||
type: actionTypes.SET_IS_SEVERITY_PICKER_OPEN,
|
||||
type: pickerType,
|
||||
payload: false,
|
||||
});
|
||||
};
|
||||
const handleStatusClickOutside = (): void => {
|
||||
dispatch({
|
||||
type: actionTypes.SET_IS_STATUS_PICKER_OPEN,
|
||||
payload: false,
|
||||
});
|
||||
};
|
||||
const handleTeamClickOutside = (): void => {
|
||||
dispatch({
|
||||
type: actionTypes.SET_IS_TEAM_PICKER_OPEN,
|
||||
payload: false,
|
||||
});
|
||||
};
|
||||
const refStatus = useOutsideClick({
|
||||
callback: handleStatusClickOutside,
|
||||
}) as MutableRefObject<HTMLDivElement>;
|
||||
|
||||
const refSeverity = useOutsideClick({
|
||||
callback: handleSevClickOutside,
|
||||
const RefPicker = (pickerType: string) => {
|
||||
return useOutsideClick({
|
||||
callback: () => handlePickerClickOutside(pickerType),
|
||||
}) as MutableRefObject<HTMLDivElement>;
|
||||
};
|
||||
|
||||
const refTeam = useOutsideClick({
|
||||
callback: handleTeamClickOutside,
|
||||
}) as MutableRefObject<HTMLDivElement>;
|
||||
const refStatus = RefPicker(actionTypes.SET_IS_STATUS_PICKER_OPEN);
|
||||
const refSeverity = RefPicker(actionTypes.SET_IS_SEVERITY_PICKER_OPEN);
|
||||
const refTeam = RefPicker(actionTypes.SET_IS_TEAM_PICKER_OPEN);
|
||||
const refProduct = RefPicker(actionTypes.SET_IS_PRODUCT_PICKER_OPEN);
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -369,7 +433,6 @@ const Dropdowns: FC = () => {
|
||||
</div>
|
||||
<div className={styles['team-selectpicker']}>
|
||||
{state.isTeamPickerOpen && (
|
||||
<div className={styles['team-selectpicker-style']}>
|
||||
<SelectPicker
|
||||
multiSelect={false}
|
||||
onSelectionChange={selectedOption =>
|
||||
@@ -377,8 +440,56 @@ const Dropdowns: FC = () => {
|
||||
}
|
||||
options={updatedTeams}
|
||||
selectedValue={incidentData?.teamId?.toString()}
|
||||
wrapperClasses={styles['team-selectpicker-style']}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles['dropdown-product']}>
|
||||
<div>
|
||||
<Typography variant="p5" color="var(--navi-color-gray-c2)">
|
||||
Product
|
||||
</Typography>
|
||||
</div>
|
||||
<div ref={refProduct}>
|
||||
<div
|
||||
className={combinedProductClassNames}
|
||||
onClick={handleDropdownClick('product')}
|
||||
>
|
||||
<Tooltip
|
||||
text={getProduct(initialProducts)}
|
||||
position="top"
|
||||
withPointer
|
||||
>
|
||||
<Typography
|
||||
variant="p4"
|
||||
color={
|
||||
isUserParticipantList
|
||||
? 'var(--navi-color-gray-c1)'
|
||||
: 'var(--navi-color-gray-c3)'
|
||||
}
|
||||
>
|
||||
{getTruncatedProduct(initialProducts)}
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
<div>
|
||||
<ArrowDownIcon className={styles['arrowdown-style']} />
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles['product-selectpicker']}>
|
||||
{state.isProductPickerOpen && (
|
||||
<SelectPicker
|
||||
multiSelect={true}
|
||||
onSelectionChange={handleProductSelectionChange}
|
||||
options={productOptions}
|
||||
selectedValues={state.selectedProducts?.map(
|
||||
product => product?.value as SelectPickerValue,
|
||||
)}
|
||||
wrapperClasses={styles['product-selectpicker-style']}
|
||||
handleCancelClick={handleProductPickerCancelClick}
|
||||
handleApplyClick={handleApplyProductSelection}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -62,7 +62,10 @@
|
||||
}
|
||||
|
||||
.dropdown-team {
|
||||
width: 240px;
|
||||
width: 210px;
|
||||
}
|
||||
.dropdown-product {
|
||||
width: 287px;
|
||||
}
|
||||
|
||||
.dropdown-box {
|
||||
@@ -102,6 +105,14 @@
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
}
|
||||
.product-selectpicker {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
.product-selectpicker-style {
|
||||
min-width: unset;
|
||||
width: 285px;
|
||||
}
|
||||
}
|
||||
|
||||
.severity-selectpicker-style {
|
||||
width: 100px;
|
||||
@@ -131,6 +142,10 @@
|
||||
.team-selectpicker {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
.team-selectpicker-style {
|
||||
min-width: unset;
|
||||
width: 208px;
|
||||
}
|
||||
}
|
||||
|
||||
.incidentChange-wrapper {
|
||||
@@ -257,3 +272,28 @@
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.product-selector {
|
||||
padding-top: 24px;
|
||||
.product-header-wrapper {
|
||||
padding-top: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.responder {
|
||||
margin-top: -5px;
|
||||
padding-right: 5px;
|
||||
.responder-tooltip {
|
||||
width: 100px;
|
||||
position: absolute;
|
||||
z-index: 10000;
|
||||
}
|
||||
}
|
||||
}
|
||||
:last-child {
|
||||
:first-child {
|
||||
width: unset !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
.product-text {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@@ -1,15 +1,31 @@
|
||||
import { FC } from 'react';
|
||||
import { FC, useEffect } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { Tooltip, Typography } from '@navi/web-ui/lib/primitives';
|
||||
import { AlertOutlineIcon } from '@navi/web-ui/lib/icons';
|
||||
import { IncidentPageState } from '@src/types';
|
||||
import Dropdowns from '../Dropdowns';
|
||||
import styles from '../Incidents.module.scss';
|
||||
import useIncidentFormApi from '@src/Pages/Dashboard/useDashboardApi';
|
||||
import { Team } from '@src/Pages/Dashboard/type';
|
||||
import { RootState } from '@src/store';
|
||||
|
||||
const UpdateIncidentBox: FC = () => {
|
||||
const { fetchAssignerAndResponder } = useIncidentFormApi();
|
||||
const incidentParticipants = useSelector(
|
||||
(state: IncidentPageState) => state.incidentLog.participantsData,
|
||||
);
|
||||
const productOptions = useSelector(
|
||||
(state: RootState) => state.incidentLog.incidentData.products,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (productOptions) {
|
||||
fetchAssignerAndResponder(
|
||||
productOptions?.map((product: Team) => product.value),
|
||||
);
|
||||
}
|
||||
}, [productOptions]);
|
||||
|
||||
const { emailId: userEmail } =
|
||||
JSON.parse(localStorage.getItem('user-data') || '{}') || {};
|
||||
const participantsList = [
|
||||
|
||||
@@ -51,10 +51,12 @@ export const actionTypes = {
|
||||
SET_OPEN_DIALOG: 'SET_OPEN_DIALOG',
|
||||
SET_OPEN_CONFIRMATION_DIALOG: 'SET_OPEN_CONFIRMATION_DIALOG',
|
||||
SET_SELECTED_OPTION: 'SET_SELECTED_OPTION',
|
||||
SET_SELECTED_PRODUCTS: 'SET_SELECTED_PRODUCTS',
|
||||
SET_UPDATE_TYPE: 'SET_UPDATE_TYPE',
|
||||
SET_DIALOG_TEXT: 'SET_DIALOG_TEXT',
|
||||
SET_DIALOG_BODY_TEXT: 'SET_DIALOG_BODY_TEXT',
|
||||
SET_IS_SEVERITY_PICKER_OPEN: 'SET_IS_SEVERITY_PICKER_OPEN',
|
||||
SET_IS_PRODUCT_PICKER_OPEN: 'SET_IS_PRODUCT_PICKER_OPEN',
|
||||
SET_IS_STATUS_PICKER_OPEN: 'SET_IS_STATUS_PICKER_OPEN',
|
||||
SET_IS_TEAM_PICKER_OPEN: 'SET_IS_TEAM_PICKER_OPEN',
|
||||
SET_DUPLICATE_DIALOG: 'SET_DUPLICATE_DIALOG',
|
||||
@@ -63,6 +65,7 @@ export const actionTypes = {
|
||||
RESET_DUPLICATE_DIALOG: 'RESET_DUPLICATE_DIALOG',
|
||||
SET_IS_INCIDENT_RESOLVED: 'SET_IS_INCIDENT_RESOLVED',
|
||||
SET_REASON: 'SET_REASON',
|
||||
SET_RESPONDERS: 'SET_RESPONDERS',
|
||||
};
|
||||
|
||||
export const reducer = (state, action) => {
|
||||
@@ -93,6 +96,8 @@ export const reducer = (state, action) => {
|
||||
return { ...state, openConfirmationDialog: action.payload };
|
||||
case actionTypes.SET_SELECTED_OPTION:
|
||||
return { ...state, selectedOption: action.payload };
|
||||
case actionTypes.SET_SELECTED_PRODUCTS:
|
||||
return { ...state, selectedProducts: action.payload };
|
||||
case actionTypes.SET_UPDATE_TYPE:
|
||||
return { ...state, updateType: action.payload };
|
||||
case actionTypes.SET_DIALOG_TEXT:
|
||||
@@ -105,6 +110,8 @@ export const reducer = (state, action) => {
|
||||
return { ...state, isStatusPickerOpen: action.payload };
|
||||
case actionTypes.SET_IS_TEAM_PICKER_OPEN:
|
||||
return { ...state, isTeamPickerOpen: action.payload };
|
||||
case actionTypes.SET_IS_PRODUCT_PICKER_OPEN:
|
||||
return { ...state, isProductPickerOpen: action.payload };
|
||||
case actionTypes.SET_INCIDENT_NAME:
|
||||
return { ...state, incidentName: action.payload };
|
||||
case actionTypes.SET_ERROR_MSG:
|
||||
@@ -120,6 +127,8 @@ export const reducer = (state, action) => {
|
||||
return { ...state, isIncidentResolved: action.payload };
|
||||
case actionTypes.SET_REASON:
|
||||
return { ...state, reason: action.payload };
|
||||
case actionTypes.SET_RESPONDERS:
|
||||
return { ...state, responderList: action.payload };
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
@@ -138,16 +147,19 @@ export const initialState = {
|
||||
openDialog: false,
|
||||
openConfirmationDialog: false,
|
||||
selectedOption: undefined,
|
||||
selectedProducts: [],
|
||||
updateType: undefined,
|
||||
dialogText: '',
|
||||
dialogBodyText: '',
|
||||
isSeverityPickerOpen: false,
|
||||
isStatusPickerOpen: false,
|
||||
isTeamPickerOpen: false,
|
||||
isProductPickerOpen: false,
|
||||
incidentName: '',
|
||||
openDuplicateDialog: false,
|
||||
errorMsg: '',
|
||||
isIncidentResolved: false,
|
||||
responderList: [],
|
||||
reason: '',
|
||||
};
|
||||
|
||||
@@ -172,3 +184,4 @@ export const DUPLICATE_STATUS = '5';
|
||||
export const SeverityType = 0;
|
||||
export const StatusType = 1;
|
||||
export const TeamType = 2;
|
||||
export const ProductType = 3;
|
||||
|
||||
@@ -7,6 +7,7 @@ import ActivityLog from './ActivityLog';
|
||||
import UpdateIncidentBox from './UpdateIncidentBox';
|
||||
import Headers from './Header';
|
||||
import useIncidentApis from './useIncidentApis';
|
||||
import useIncidentFormApi from '@src/Pages/Dashboard/useDashboardApi';
|
||||
import styles from './Incidents.module.scss';
|
||||
|
||||
const Incident: FC = () => {
|
||||
@@ -17,10 +18,12 @@ const Incident: FC = () => {
|
||||
const incidentId = match?.params?.incidentId || '';
|
||||
|
||||
const { startIncidentSearch, fetchHeaderDetails } = useIncidentApis();
|
||||
const { fetchProductList } = useIncidentFormApi();
|
||||
|
||||
useEffect(() => {
|
||||
startIncidentSearch(incidentId);
|
||||
fetchHeaderDetails();
|
||||
fetchProductList();
|
||||
}, [incidentId]);
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { SeverityType, StatusType, TeamType } from './constants';
|
||||
import { ProductType, SeverityType, StatusType, TeamType } from './constants';
|
||||
export const getUpdateTypeText = (updateType: number): string => {
|
||||
switch (updateType) {
|
||||
case SeverityType:
|
||||
@@ -7,8 +7,10 @@ export const getUpdateTypeText = (updateType: number): string => {
|
||||
return 'status';
|
||||
case TeamType:
|
||||
return 'team';
|
||||
case ProductType:
|
||||
return 'product';
|
||||
default:
|
||||
return 'severity';
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
@@ -26,12 +28,14 @@ interface Maps {
|
||||
severityMap: { [key: number]: string };
|
||||
statusMap: { [key: number]: string };
|
||||
teamsMap: { [key: number]: string };
|
||||
productMap: { [key: number]: string };
|
||||
}
|
||||
|
||||
interface InitialValues {
|
||||
initialSeverity?: { value: number | null };
|
||||
initialStatus?: { value: number | null };
|
||||
initialTeam?: { value: number | null };
|
||||
initialProducts?: { label: string; value: number }[] | null;
|
||||
}
|
||||
|
||||
export const generateOptions = (data: DataItem[]): DataItemModified[] => {
|
||||
@@ -60,7 +64,7 @@ export const linkSanitization = (link: string): string => {
|
||||
};
|
||||
|
||||
export const getCurrentData = (
|
||||
data: { [key: string]: any },
|
||||
data: { [key: string]: any } | null,
|
||||
dataMap: { [key: string]: string },
|
||||
key: string,
|
||||
): string => {
|
||||
@@ -102,11 +106,17 @@ export const getUpdateValueText = ({
|
||||
? maps.teamsMap[initialValues.initialTeam.value]
|
||||
: '-'
|
||||
} `;
|
||||
default:
|
||||
case ProductType:
|
||||
return ` ${
|
||||
maps.severityMap && initialValues.initialSeverity?.value
|
||||
? maps.severityMap[initialValues.initialSeverity.value]
|
||||
initialValues.initialProducts?.length
|
||||
? initialValues.initialProducts
|
||||
.map(
|
||||
(product: { value: number }) => maps.productMap[product.value],
|
||||
)
|
||||
.join(', ')
|
||||
: '-'
|
||||
} `;
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
@@ -42,6 +42,8 @@ const initialState: IncidentLogPageState = {
|
||||
createdAt: null,
|
||||
updatedAt: null,
|
||||
rcaLink: '',
|
||||
assignerTeam: null,
|
||||
products: null,
|
||||
},
|
||||
headerData: {
|
||||
severities: [],
|
||||
|
||||
6
src/types/index.d.ts
vendored
6
src/types/index.d.ts
vendored
@@ -81,8 +81,8 @@ export interface IncidentDatatype {
|
||||
createdAt: Date | null;
|
||||
updatedAt: Date | null;
|
||||
rcaLink: string;
|
||||
assignerTeam: Team;
|
||||
products: Team[];
|
||||
assignerTeam: Team | null;
|
||||
products: Team[] | null;
|
||||
}
|
||||
|
||||
interface Severity {
|
||||
@@ -179,4 +179,6 @@ export interface UpdateIncidentType {
|
||||
teamId?: string;
|
||||
status?: string;
|
||||
justification?: string;
|
||||
productIds?: number[];
|
||||
teamId?: string;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user