INFRA-2803 | Saurabh | De-escalation justification message (#128)
* INFRA-2803 | Saurabh | Added textarea for justification message * INFRA-2803 | Saurabh | Showing the message in activiy log * INFRA-2803 | Saurabh | Added max character limit and footer updated with tooltip * INFRA-2803 | Saurabh | wrapped using break word * INFRA-2803 | Saurabh | styles in css * INFRA-2803 | Saurabh | custom Footer class created * INFRA-2803 | Saurabh | comments resolved
This commit is contained in:
committed by
GitHub
parent
5d77e0613b
commit
05e1afb108
@@ -7,6 +7,7 @@ import {
|
||||
ModalDialog,
|
||||
Typography,
|
||||
Button,
|
||||
Tooltip,
|
||||
} from '@navi/web-ui/lib/primitives';
|
||||
import { toast } from '@navi/web-ui/lib/primitives/Toast';
|
||||
import GoToLinkIcon from '@src/assets/GoToLinkIcon';
|
||||
@@ -25,6 +26,7 @@ import {
|
||||
initialState,
|
||||
incidentRegrex,
|
||||
RESOLVE_STATUS,
|
||||
getSeverityValue,
|
||||
} from '../constants';
|
||||
import useIncidentApis from '../useIncidentApis';
|
||||
import styles from '../Incidents.module.scss';
|
||||
@@ -57,6 +59,13 @@ const AllDailogBox: FC = () => {
|
||||
const incidentId = incidentData?.id?.toString() || '';
|
||||
const reduxDispatch = useDispatch();
|
||||
|
||||
const updateDetailsFrom = getSeverityValue(UpdateData?.from?.trim() || '');
|
||||
const updateDetailsTo = (
|
||||
UpdateData as { to?: { value: string; label: string } }
|
||||
)?.to?.value;
|
||||
const showJustificationBox: boolean =
|
||||
updateDetailsFrom < (updateDetailsTo || '-1') || false;
|
||||
|
||||
const handleopenNotParticipants = (): void => {
|
||||
reduxDispatch(setOpenDialognotParticipants(false));
|
||||
};
|
||||
@@ -97,6 +106,15 @@ const AllDailogBox: FC = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const handleReasonChange = (
|
||||
e: React.ChangeEvent<HTMLTextAreaElement>,
|
||||
): void => {
|
||||
dispatch({
|
||||
type: actionTypes.SET_REASON,
|
||||
payload: e.target.value,
|
||||
});
|
||||
};
|
||||
|
||||
const validateIncidentID = (value: string): void => {
|
||||
dispatch({
|
||||
type: actionTypes.SET_ERROR_MSG,
|
||||
@@ -105,6 +123,12 @@ const AllDailogBox: FC = () => {
|
||||
: '',
|
||||
});
|
||||
};
|
||||
|
||||
const handleUpdateIncidentClick = (): void => {
|
||||
reduxDispatch(setOpenDialogUpdate(false));
|
||||
handleCloseConfirmationDialog();
|
||||
};
|
||||
|
||||
const validate = (value: string): boolean => incidentRegrex.test(value);
|
||||
const isDisabled = (): boolean => {
|
||||
const incidentId = extractIncidentId(state.incidentName);
|
||||
@@ -132,10 +156,23 @@ const AllDailogBox: FC = () => {
|
||||
const value = Array.isArray(selectedOption)
|
||||
? selectedOption[0].value
|
||||
: selectedOption.value;
|
||||
updateIncident({
|
||||
id: parseInt(incidentId, 10),
|
||||
severityId: value.toString(),
|
||||
});
|
||||
|
||||
if (showJustificationBox) {
|
||||
updateIncident({
|
||||
id: parseInt(incidentId, 10),
|
||||
severityId: value.toString(),
|
||||
justification: state.reason,
|
||||
});
|
||||
dispatch({
|
||||
type: actionTypes.SET_REASON,
|
||||
payload: '',
|
||||
});
|
||||
} else {
|
||||
updateIncident({
|
||||
id: parseInt(incidentId, 10),
|
||||
severityId: value.toString(),
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
const handleStatusSelectionChange = (
|
||||
@@ -321,26 +358,26 @@ const AllDailogBox: FC = () => {
|
||||
</div>
|
||||
);
|
||||
|
||||
const customFooter = (): JSX.Element => (
|
||||
<div className={styles['modal-footer']}>
|
||||
{UpdateData.type === 'severity' &&
|
||||
showJustificationBox &&
|
||||
state.reason.length <= 0 ? (
|
||||
<Tooltip text="Please enter a reason for de-escalation" position="left">
|
||||
<Button disabled>Update incident</Button>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Button onClick={handleUpdateIncidentClick}>Update incident</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
const renderUpdateDialog = (): JSX.Element => (
|
||||
<div>
|
||||
{openUpdate ? (
|
||||
<ModalDialog
|
||||
open={openUpdate}
|
||||
footerButtons={[
|
||||
{
|
||||
label: 'Cancel',
|
||||
onClick: () => {
|
||||
reduxDispatch(setOpenDialogUpdate(false));
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Update incident',
|
||||
onClick: () => {
|
||||
reduxDispatch(setOpenDialogUpdate(false));
|
||||
handleCloseConfirmationDialog();
|
||||
},
|
||||
},
|
||||
]}
|
||||
customFooter={customFooter()}
|
||||
header={`Are you sure you want to update the incident?`}
|
||||
onClose={() => reduxDispatch(setOpenDialogUpdate(false))}
|
||||
>
|
||||
@@ -365,6 +402,25 @@ const AllDailogBox: FC = () => {
|
||||
{getLabelFromOption(UpdateData?.to) || '..'}
|
||||
</Typography>
|
||||
</Typography>
|
||||
{UpdateData.type === 'severity' && showJustificationBox ? (
|
||||
<div>
|
||||
<textarea
|
||||
placeholder="Enter reason for de-escalation"
|
||||
value={state.reason}
|
||||
className={styles['incident-justification-box']}
|
||||
onChange={handleReasonChange}
|
||||
maxLength={100}
|
||||
/>
|
||||
<Typography
|
||||
variant="p5"
|
||||
className={
|
||||
styles['incident-justification-box-character-limit']
|
||||
}
|
||||
>
|
||||
{state.reason.length} / 100 characters
|
||||
</Typography>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</ModalDialog>
|
||||
) : null}
|
||||
|
||||
@@ -4,7 +4,12 @@ import { returnFormattedDate } from '@src/services/globalUtils';
|
||||
import { UpdateInfoProps } from '../types';
|
||||
import styles from '../Incidents.module.scss';
|
||||
|
||||
const renderUpdateInfo = (fromState, byPerson, updatedAt): JSX.Element => (
|
||||
const renderUpdateInfo = (
|
||||
fromState,
|
||||
byPerson,
|
||||
updatedAt,
|
||||
reason,
|
||||
): JSX.Element => (
|
||||
<>
|
||||
<div className={styles['update-info-firstline']}>
|
||||
<div className={styles['single-line-wrapper']}>
|
||||
@@ -27,6 +32,14 @@ const renderUpdateInfo = (fromState, byPerson, updatedAt): JSX.Element => (
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{reason !== '' && (
|
||||
<div className={styles['create-slack-info']} style={{ padding: '12px' }}>
|
||||
<Typography variant="p5" color="var(--navi-color-gray-c3)">
|
||||
Reason : <span className={styles['reason-text']}>{reason}</span>
|
||||
</Typography>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className={styles['timestamp-wrapper']}>
|
||||
<Typography variant="p5" color="var(--navi-color-gray-c3)">
|
||||
at
|
||||
@@ -42,14 +55,15 @@ const UpdateInfo: FC<UpdateInfoProps> = ({
|
||||
byPerson,
|
||||
updatedAt,
|
||||
isLastItem,
|
||||
reason,
|
||||
}) => (
|
||||
<div className={styles['created-info-wrapper']}>
|
||||
{isLastItem ? (
|
||||
<div className={styles['last-log-wrapper']}>
|
||||
{renderUpdateInfo(fromState, byPerson, updatedAt)}
|
||||
{renderUpdateInfo(fromState, byPerson, updatedAt, reason)}
|
||||
</div>
|
||||
) : (
|
||||
renderUpdateInfo(fromState, byPerson, updatedAt)
|
||||
renderUpdateInfo(fromState, byPerson, updatedAt, reason)
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -231,3 +231,28 @@
|
||||
@include flex-center;
|
||||
}
|
||||
}
|
||||
|
||||
.incident-justification-box {
|
||||
width: 400px;
|
||||
resize: vertical;
|
||||
height: 50px;
|
||||
border-radius: 8px;
|
||||
margin-top: 12px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.incident-justification-box-character-limit {
|
||||
color: var(--navi-color-gray-c2);
|
||||
text-align: right;
|
||||
width: 425px;
|
||||
}
|
||||
|
||||
.reason-text {
|
||||
color: var(--navi-color-gray-c1);
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
@@ -80,6 +80,7 @@ const IncidentChange: FC<IncidentChangeProps> = ({
|
||||
byPerson={item.user_info ? item.user_info.name : 'N/A'}
|
||||
updatedAt={item.created_at}
|
||||
isLastItem={isLastItem}
|
||||
reason={item?.justification || ''}
|
||||
/>
|
||||
}
|
||||
stageIcon={getStageIcon(isSevChange, isStatusChange, change) || null}
|
||||
|
||||
@@ -62,6 +62,7 @@ export const actionTypes = {
|
||||
SET_ERROR_MSG: 'SET_ERROR_MSG',
|
||||
RESET_DUPLICATE_DIALOG: 'RESET_DUPLICATE_DIALOG',
|
||||
SET_IS_INCIDENT_RESOLVED: 'SET_IS_INCIDENT_RESOLVED',
|
||||
SET_REASON: 'SET_REASON',
|
||||
};
|
||||
|
||||
export const reducer = (state, action) => {
|
||||
@@ -117,6 +118,8 @@ export const reducer = (state, action) => {
|
||||
};
|
||||
case actionTypes.SET_IS_INCIDENT_RESOLVED:
|
||||
return { ...state, isIncidentResolved: action.payload };
|
||||
case actionTypes.SET_REASON:
|
||||
return { ...state, reason: action.payload };
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
@@ -145,6 +148,23 @@ export const initialState = {
|
||||
openDuplicateDialog: false,
|
||||
errorMsg: '',
|
||||
isIncidentResolved: false,
|
||||
reason: '',
|
||||
};
|
||||
|
||||
export const getSeverityValue = (value: string): string => {
|
||||
switch (value) {
|
||||
case 'Sev-0':
|
||||
return '1';
|
||||
case 'Sev-1':
|
||||
return '2';
|
||||
case 'Sev-2':
|
||||
return '3';
|
||||
case 'Sev-3':
|
||||
return '4';
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return '-1';
|
||||
};
|
||||
|
||||
export const RESOLVE_STATUS = '4';
|
||||
|
||||
@@ -19,6 +19,7 @@ export interface UpdateInfoProps {
|
||||
byPerson: string;
|
||||
updatedAt: string;
|
||||
isLastItem: boolean;
|
||||
reason: string;
|
||||
}
|
||||
|
||||
export interface CreatedInfoProps {
|
||||
|
||||
@@ -32,7 +32,6 @@ const useTeamApis = (): useTeamApiProps => {
|
||||
const finalSlackData = emailIds.includes(',')
|
||||
? emailIds.split(',').map(item => item?.trim())
|
||||
: [emailIds];
|
||||
console.log(teamId, emailIds, finalSlackData);
|
||||
ApiService.post(endPoint, {
|
||||
id: teamId,
|
||||
workEmailIds: finalSlackData,
|
||||
|
||||
1
src/types/index.d.ts
vendored
1
src/types/index.d.ts
vendored
@@ -176,4 +176,5 @@ export interface UpdateIncidentType {
|
||||
severityId?: string;
|
||||
teamId?: string;
|
||||
status?: string;
|
||||
justification?: string;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user