TP-49551 | Create Incident from UI (#101)
* TP-49551 | added functionalities for creating incident * TP-49551 | styling added * TP-49551 | added selectpicker and changed endpoint * TP-49551 | added input validations , selectpicker position * TP-49551 | added loader and minor css changes * TP-49551 | title and desc placeholder correction * TP-49551 | added clickstream and some design review changes * TP-49551 | design review and pr comments changes * TP-49551 | PR comments changes * TP-49551 | pr reviews'2 * TP-49551 | implemented container pattern * TP-49551 | add PR template * TP-49551 | added PR template * TP-49551 | updated imports
This commit is contained in:
18
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
18
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
**PR Checklist to be followed:**
|
||||
|
||||
**Checklist:**
|
||||
|
||||
- [ ] Imports to be ordered : React library =>Other libraries => Other Folders => Current Folder
|
||||
- [ ] Commented code should be removed(Write TODO if required)
|
||||
- [ ] Aliasing of imports is preferred
|
||||
- [ ] Don’t hard code URL
|
||||
- [ ] No camel casing in css, use hyphens (example: use-like-this)
|
||||
- [ ] Maintain consistency while writing dimensions
|
||||
- [ ] Avoid console.log
|
||||
- [ ] Use global utils if its common for many pages, if not maintain in the folder level
|
||||
- [ ] DateFormat should be followed same
|
||||
- [ ] Avoid inline styling
|
||||
- [ ] Proper TS convention ( don’t use any, instead define interface and use it)
|
||||
- [ ] Peer review
|
||||
- [ ] Font-family / Basic css shouldn’t be overridden
|
||||
- [ ] File changes shouldn’t be more than 20-30, if its more than 25 please create a draft PR and share it so it can reviewed properly.
|
||||
@@ -7,3 +7,15 @@
|
||||
.dashboard-wrapper {
|
||||
padding: 24px;
|
||||
}
|
||||
.dashboard-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.header-wrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ export const FETCH_INCIDENTS_DATA = (payload: string): string => {
|
||||
|
||||
export const FETCH_FILTER_CONFIG = `${URL_PREFIX}/filters`;
|
||||
|
||||
export const CREATE_INCIDENT = createURL('/houston/create-incident-v2');
|
||||
|
||||
export const TimeSpan = [
|
||||
{
|
||||
label: 'Last 24 hours',
|
||||
@@ -55,3 +57,68 @@ export const getSeverityColor = (value: string) => {
|
||||
break;
|
||||
}
|
||||
};
|
||||
export const LIMITS = {
|
||||
title: { max: 100 },
|
||||
description: { max: 500 },
|
||||
};
|
||||
export const actionTypes = {
|
||||
SET_DRAWER_OPEN: 'SET_DRAWER_OPEN',
|
||||
SET_TEAMS: 'SET_TEAMS',
|
||||
SET_TITLE: 'SET_TITLE',
|
||||
SET_SEVERITY: 'SET_SEVERITY',
|
||||
SET_SELECTED_SEVERITY: 'SET_SELECTED_SEVERITY',
|
||||
SET_DESCRIPTION: 'SET_DESCRIPTION',
|
||||
SET_SELECTED_TEAM: 'SET_SELECTED_TEAM',
|
||||
SET_OPEN_SELECT: 'SET_OPEN_SELECT',
|
||||
SET_IS_TITLE_VALID: 'SET_IS_TITLE_VALID',
|
||||
SET_IS_DESCRIPTION_VALID: 'SET_IS_DESCRIPTION_VALID',
|
||||
CLEAR_DRAWER: 'CLEAR_DRAWER',
|
||||
};
|
||||
|
||||
export const initialState = {
|
||||
open: false,
|
||||
teams: [],
|
||||
title: '',
|
||||
severity: [],
|
||||
selectedSeverity: [],
|
||||
description: '',
|
||||
selectedTeam: [],
|
||||
openSelect: false,
|
||||
isTitleValid: true,
|
||||
isDescriptionValid: true,
|
||||
};
|
||||
export const reducer = (state, action) => {
|
||||
switch (action.type) {
|
||||
case actionTypes.SET_TEAMS:
|
||||
return { ...state, teams: action.payload };
|
||||
case actionTypes.SET_TITLE:
|
||||
return { ...state, title: action.payload };
|
||||
case actionTypes.SET_SEVERITY:
|
||||
return { ...state, severity: action.payload };
|
||||
case actionTypes.SET_SELECTED_SEVERITY:
|
||||
return { ...state, selectedSeverity: action.payload };
|
||||
case actionTypes.SET_DESCRIPTION:
|
||||
return { ...state, description: action.payload };
|
||||
case actionTypes.SET_SELECTED_TEAM:
|
||||
return { ...state, selectedTeam: action.payload };
|
||||
case actionTypes.SET_OPEN_SELECT:
|
||||
return { ...state, openSelect: action.payload };
|
||||
case actionTypes.SET_IS_TITLE_VALID:
|
||||
return { ...state, isTitleValid: action.payload };
|
||||
case actionTypes.SET_IS_DESCRIPTION_VALID:
|
||||
return { ...state, isDescriptionValid: action.payload };
|
||||
case actionTypes.SET_DRAWER_OPEN:
|
||||
return { ...state, open: action.payload };
|
||||
case actionTypes.CLEAR_DRAWER:
|
||||
return {
|
||||
...state,
|
||||
title: '',
|
||||
selectedSeverity: null,
|
||||
selectedTeam: [],
|
||||
description: '',
|
||||
openSelect: false,
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -122,7 +122,11 @@ const Dashboard: FC = () => {
|
||||
|
||||
return (
|
||||
<div className={styles['dashboard-wrapper']}>
|
||||
<DashboardHeader fetchIncidentData={fetchIncidentData} />
|
||||
<DashboardHeader
|
||||
fetchIncidentData={fetchIncidentData}
|
||||
startIncidentSearch={startIncidentSearch}
|
||||
/>
|
||||
|
||||
{returnTable()}
|
||||
</div>
|
||||
);
|
||||
|
||||
130
src/Pages/Dashboard/partials/CreateIncident.module.scss
Normal file
130
src/Pages/Dashboard/partials/CreateIncident.module.scss
Normal file
@@ -0,0 +1,130 @@
|
||||
@mixin flex-center {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@mixin wrapper-styles {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
@mixin button-styles {
|
||||
cursor: pointer;
|
||||
height: 36px;
|
||||
}
|
||||
.name-wrapper,
|
||||
.Desc-name,
|
||||
.incident-name {
|
||||
color: var(--navi-color-gray-c2);
|
||||
}
|
||||
.name-wrapper {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.Desc-name {
|
||||
margin-bottom: 160px;
|
||||
}
|
||||
|
||||
.incident-name {
|
||||
margin-bottom: 65px;
|
||||
}
|
||||
|
||||
.drawer {
|
||||
width: 550px !important;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: 16px !important;
|
||||
|
||||
> span {
|
||||
@include flex-center;
|
||||
}
|
||||
}
|
||||
|
||||
.textarea {
|
||||
min-width: 350px !important;
|
||||
color: var(--navi-color-gray-c2);
|
||||
|
||||
> div > textarea {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
.create-incident {
|
||||
background: var(--navi-color-blue-base);
|
||||
@include button-styles;
|
||||
width: 153px;
|
||||
margin: 20px 0 0 40px;
|
||||
}
|
||||
|
||||
.create,
|
||||
.cancel {
|
||||
@include button-styles;
|
||||
width: 231px;
|
||||
}
|
||||
|
||||
.footer-wrapper {
|
||||
@include flex-center;
|
||||
margin-top: 380px;
|
||||
}
|
||||
|
||||
.incident-wrapper,
|
||||
.Description-wrapper,
|
||||
.severity-picker,
|
||||
.Team-wrapper {
|
||||
@include wrapper-styles;
|
||||
}
|
||||
|
||||
.severity-wrapper {
|
||||
width: calc(100% - 156px);
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
gap: 0 16px;
|
||||
padding: 0 8px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.team-div {
|
||||
width: 352px;
|
||||
height: 36px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: 1px solid var(--navi-color-gray-border);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.select-team {
|
||||
position: relative;
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 0 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.select-picker {
|
||||
border-radius: 4px;
|
||||
position: fixed;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.select-picker-style {
|
||||
position: absolute;
|
||||
margin-top: 25px;
|
||||
margin-left: 152px;
|
||||
cursor: pointer;
|
||||
z-index: 1;
|
||||
}
|
||||
.select-picker-item {
|
||||
min-width: 352px;
|
||||
margin-top: 2px;
|
||||
margin-left: 14px;
|
||||
}
|
||||
|
||||
.arrow-down {
|
||||
display: inline;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
67
src/Pages/Dashboard/partials/CreateIncident.tsx
Normal file
67
src/Pages/Dashboard/partials/CreateIncident.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
import { useReducer } from 'react';
|
||||
import { Button, Drawer } from '@navi/web-ui/lib/primitives';
|
||||
import { AddIcon } from '@navi/web-ui/lib/icons';
|
||||
import ErrorBoundary from '@src/components/ErrorBoundary/ErrorBoundary';
|
||||
import useClickStream from '@src/services/clickStream';
|
||||
import { CLICK_STREAM_EVENT_FACTORY } from '@src/services/clickStream/constants/values';
|
||||
import {
|
||||
actionTypes,
|
||||
initialState,
|
||||
reducer,
|
||||
} from '@src/Pages/Dashboard/constants';
|
||||
import CreateIncidentForm from '@src/Pages/Dashboard/partials/CreateIncidentForm';
|
||||
import styles from './CreateIncident.module.scss';
|
||||
|
||||
interface CreateIncidentProps {
|
||||
startIncidentSearch: () => void;
|
||||
}
|
||||
|
||||
const CreateIncident: React.FC<CreateIncidentProps> = ({
|
||||
startIncidentSearch,
|
||||
}) => {
|
||||
const [state, dispatch] = useReducer(reducer, initialState);
|
||||
const { fireEvent } = useClickStream();
|
||||
const { EVENT_NAME, SCREEN_NAME } = CLICK_STREAM_EVENT_FACTORY;
|
||||
|
||||
const handleDrawerOpen = () => {
|
||||
fireEvent(EVENT_NAME.Houston_Create_Incident_initiate, {
|
||||
screen_name: SCREEN_NAME.DASHBOARD_PAGE,
|
||||
});
|
||||
dispatch({ type: actionTypes.SET_DRAWER_OPEN, payload: true });
|
||||
};
|
||||
const handleDrawerClose = () => {
|
||||
dispatch({ type: actionTypes.SET_DRAWER_OPEN, payload: false });
|
||||
};
|
||||
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<div>
|
||||
<Button
|
||||
startAdornment={<AddIcon color="white" />}
|
||||
fullWidth
|
||||
onClick={handleDrawerOpen}
|
||||
variant="primary"
|
||||
className={styles['create-incident']}
|
||||
>
|
||||
Create incident
|
||||
</Button>
|
||||
<Drawer
|
||||
open={state.open}
|
||||
headerText="Create incident"
|
||||
className={`scroll-smooth ${styles.drawer}`}
|
||||
headerContainerClasses={styles.header}
|
||||
onClose={handleDrawerClose}
|
||||
onOutSideClick={handleDrawerClose}
|
||||
showHeaderDivider
|
||||
>
|
||||
<CreateIncidentForm
|
||||
startIncidentSearch={startIncidentSearch}
|
||||
handleDrawerClose={handleDrawerClose}
|
||||
/>
|
||||
</Drawer>
|
||||
</div>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateIncident;
|
||||
289
src/Pages/Dashboard/partials/CreateIncidentForm.tsx
Normal file
289
src/Pages/Dashboard/partials/CreateIncidentForm.tsx
Normal file
@@ -0,0 +1,289 @@
|
||||
import { useReducer, MutableRefObject, useEffect, useState } from 'react';
|
||||
import { ArrowDownSolidIcon } from '@navi/web-ui/lib/icons';
|
||||
import { SelectPicker } from '@navi/web-ui/lib/components';
|
||||
import { toast } from '@navi/web-ui/lib/primitives/Toast';
|
||||
import {
|
||||
Button,
|
||||
Chip,
|
||||
TextArea,
|
||||
Typography,
|
||||
} from '@navi/web-ui/lib/primitives';
|
||||
import { SelectPickerOptionProps } from '@navi/web-ui/lib/components/SelectPicker/types';
|
||||
import useOutsideClick from '@src/hooks/useOutsideClick';
|
||||
import LoadingIcon from '@src/assets/LoadingIcon';
|
||||
import { ApiService } from '@src/services/api';
|
||||
|
||||
import {
|
||||
CREATE_INCIDENT,
|
||||
FETCH_FILTER_CONFIG,
|
||||
LIMITS as LIMITS,
|
||||
actionTypes,
|
||||
initialState,
|
||||
reducer,
|
||||
} from '@src/Pages/Dashboard/constants';
|
||||
import styles from './CreateIncident.module.scss';
|
||||
import { Team } from '@src/Pages/Dashboard/type';
|
||||
import useClickStream from '@src/services/clickStream';
|
||||
import { CLICK_STREAM_EVENT_FACTORY } from '@src/services/clickStream/constants/values';
|
||||
|
||||
interface CreateIncidentFormProps {
|
||||
startIncidentSearch: () => void;
|
||||
handleDrawerClose: () => void;
|
||||
}
|
||||
const CreateIncidentForm: React.FC<CreateIncidentFormProps> = ({
|
||||
startIncidentSearch,
|
||||
handleDrawerClose,
|
||||
}) => {
|
||||
const [state, dispatch] = useReducer(reducer, initialState);
|
||||
const [loading, setIsLoading] = useState(false);
|
||||
const { fireEvent } = useClickStream();
|
||||
const { EVENT_NAME, SCREEN_NAME } = CLICK_STREAM_EVENT_FACTORY;
|
||||
const ref = useOutsideClick({
|
||||
callback: handleClickOutside,
|
||||
}) as MutableRefObject<HTMLDivElement>;
|
||||
function handleClickOutside() {
|
||||
dispatch({ type: actionTypes.SET_OPEN_SELECT, payload: false });
|
||||
}
|
||||
const handleDivClick = () => {
|
||||
dispatch({ type: actionTypes.SET_OPEN_SELECT, payload: true });
|
||||
};
|
||||
|
||||
const validateTitle = (value: string): void => {
|
||||
dispatch({
|
||||
type: actionTypes.SET_IS_TITLE_VALID,
|
||||
payload: value.length <= LIMITS.title.max,
|
||||
});
|
||||
};
|
||||
const userData = JSON.parse(localStorage.getItem('user-data') || '{}');
|
||||
const userEmail = userData?.emailId || [];
|
||||
const validateDescription = (value: string): void => {
|
||||
dispatch({
|
||||
type: actionTypes.SET_IS_DESCRIPTION_VALID,
|
||||
payload: value.length <= LIMITS.description.max,
|
||||
});
|
||||
};
|
||||
|
||||
const handleSeverityChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
dispatch({ type: actionTypes.SET_SELECTED_SEVERITY, payload: event });
|
||||
};
|
||||
const handleTitleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const inputValue = event.target.value;
|
||||
dispatch({ type: actionTypes.SET_TITLE, payload: inputValue });
|
||||
validateTitle(inputValue);
|
||||
};
|
||||
const handleTeamChange = (
|
||||
val: SelectPickerOptionProps | SelectPickerOptionProps[],
|
||||
) => {
|
||||
dispatch({ type: actionTypes.SET_SELECTED_TEAM, payload: val });
|
||||
dispatch({ type: actionTypes.SET_OPEN_SELECT, payload: false });
|
||||
};
|
||||
const handleDescriptionChange = (
|
||||
event: React.ChangeEvent<HTMLInputElement>,
|
||||
) => {
|
||||
const description = event.target.value;
|
||||
dispatch({
|
||||
type: actionTypes.SET_DESCRIPTION,
|
||||
payload: event.target.value,
|
||||
});
|
||||
validateDescription(description);
|
||||
};
|
||||
|
||||
const getData = () => {
|
||||
ApiService.get(FETCH_FILTER_CONFIG)
|
||||
.then(response => {
|
||||
const apiResponse = response?.data?.data;
|
||||
if (!apiResponse) {
|
||||
toast.error('Something went wrong. Please try again later');
|
||||
return;
|
||||
}
|
||||
dispatch({
|
||||
type: actionTypes.SET_TEAMS,
|
||||
payload: apiResponse[2]?.filter_data,
|
||||
});
|
||||
dispatch({
|
||||
type: actionTypes.SET_SEVERITY,
|
||||
payload: apiResponse[1]?.filter_data || [],
|
||||
});
|
||||
dispatch({
|
||||
type: actionTypes.SET_SELECTED_SEVERITY,
|
||||
payload: apiResponse[1]?.filter_data.map(item => item.label) || [],
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
const toastMessage = error?.response?.data?.error?.message
|
||||
? `${error?.response?.data?.error?.message},`
|
||||
: '';
|
||||
toast.error(toastMessage);
|
||||
});
|
||||
};
|
||||
useEffect(() => {
|
||||
getData();
|
||||
}, []);
|
||||
|
||||
const getTeams = (teamsState: Team[]) => {
|
||||
return teamsState.map(team => ({
|
||||
label: team.label,
|
||||
value: team.value,
|
||||
}));
|
||||
};
|
||||
const clearDrawer = () => {
|
||||
dispatch({ type: actionTypes.CLEAR_DRAWER });
|
||||
handleDrawerClose();
|
||||
};
|
||||
const isDisabled = () => {
|
||||
return (
|
||||
!state.isTitleValid ||
|
||||
!state.isDescriptionValid ||
|
||||
!state.title ||
|
||||
!state.selectedSeverity ||
|
||||
!state.selectedTeam ||
|
||||
!state.description
|
||||
);
|
||||
};
|
||||
|
||||
const createIncidentHandler = () => {
|
||||
toast('Creating incident. Please wait a moment.', {
|
||||
icon: <LoadingIcon />,
|
||||
});
|
||||
|
||||
fireEvent(EVENT_NAME.Houston_Create_Incident_submit, {
|
||||
screen_name: SCREEN_NAME.DASHBOARD_PAGE,
|
||||
});
|
||||
|
||||
ApiService.post(CREATE_INCIDENT, {
|
||||
title: state.title,
|
||||
severity: state.selectedSeverity.value,
|
||||
type: state.selectedTeam.value,
|
||||
description: state.description,
|
||||
createdBy: userEmail,
|
||||
})
|
||||
.then(response => {
|
||||
setIsLoading(false);
|
||||
toast.success('Incident created successfully');
|
||||
clearDrawer();
|
||||
startIncidentSearch();
|
||||
})
|
||||
.catch(error => {
|
||||
const toastMessage = `${
|
||||
error?.response?.data?.error?.message
|
||||
? `${error?.response?.data?.error?.message}`
|
||||
: 'Something went wrong. Please try again later'
|
||||
}`;
|
||||
toast.error(toastMessage);
|
||||
handleDrawerClose();
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={styles['incident-wrapper']}>
|
||||
<div className={styles['incident-name']}>Incident title</div>
|
||||
<TextArea
|
||||
name="title"
|
||||
placeholder="Enter incident title"
|
||||
containerClassName={styles.textarea}
|
||||
rows={3}
|
||||
onChange={handleTitleChange as any}
|
||||
value={state.title}
|
||||
fullWidth
|
||||
error={
|
||||
!state.isTitleValid
|
||||
? `Title should be less than ${LIMITS.title.max} characters`
|
||||
: ''
|
||||
}
|
||||
size="large"
|
||||
/>
|
||||
</div>
|
||||
<div className={styles['severity-picker']}>
|
||||
<div className={styles['name-wrapper']}>Choose severity</div>
|
||||
<div className={styles['severity-wrapper']}>
|
||||
{state.severity.map((severityOption: SelectPickerOptionProps) => (
|
||||
<Chip
|
||||
key={severityOption.value}
|
||||
label={severityOption.label}
|
||||
size="medium"
|
||||
onChipClick={() => handleSeverityChange(severityOption as any)}
|
||||
selected={severityOption === state.selectedSeverity}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles['Team-wrapper']}>
|
||||
<div className={styles['name-wrapper']}>Choose team</div>
|
||||
|
||||
<div className={styles['team-div']} onClick={handleDivClick}>
|
||||
<div className={styles['select-team']}>
|
||||
<Typography
|
||||
variant="p3"
|
||||
color={
|
||||
state.selectedTeam.length === 0
|
||||
? 'var(--navi-color-gray-c2)'
|
||||
: 'var(--navi-color-gray-c1)'
|
||||
}
|
||||
>
|
||||
{state.selectedTeam.length === 0
|
||||
? 'Select team'
|
||||
: ` ${state.selectedTeam.label}`}
|
||||
</Typography>
|
||||
<ArrowDownSolidIcon
|
||||
color="var(--navi-color-gray-c3)"
|
||||
className={styles['arrow-down']}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles['select-picker-style']}>
|
||||
{state.openSelect && (
|
||||
<div className={styles['select-picker']} ref={ref}>
|
||||
<SelectPicker
|
||||
options={getTeams(state.teams)}
|
||||
placeholder="Select team"
|
||||
onSelectionChange={handleTeamChange}
|
||||
multiSelect={false}
|
||||
showSearchBar
|
||||
wrapperClasses={styles['select-picker-item']}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles['Description-wrapper']}>
|
||||
<div className={styles['Desc-name']}>Description</div>
|
||||
<TextArea
|
||||
name="title"
|
||||
placeholder="Enter incident description"
|
||||
containerClassName={styles.textarea}
|
||||
rows={7}
|
||||
value={state.description}
|
||||
onChange={handleDescriptionChange as any}
|
||||
error={
|
||||
!state.isDescriptionValid
|
||||
? `Description should be less than ${LIMITS.description.max} characters`
|
||||
: ''
|
||||
}
|
||||
fullWidth
|
||||
size="large"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles['footer-wrapper']}>
|
||||
<div className={styles['cancel']}>
|
||||
<Button variant="secondary" fullWidth onClick={clearDrawer}>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
<div className={styles['create']}>
|
||||
<Button
|
||||
variant="primary"
|
||||
fullWidth
|
||||
onClick={() => createIncidentHandler()}
|
||||
disabled={isDisabled()}
|
||||
>
|
||||
Create incident
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateIncidentForm;
|
||||
@@ -143,4 +143,6 @@
|
||||
|
||||
.filter {
|
||||
margin-top: 20px;
|
||||
margin-left: 30px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
@@ -7,12 +7,17 @@ import { DashboardHeaderConstants } from '../constants';
|
||||
import styles from './DashboardHeader.module.scss';
|
||||
import SmartSearch from './SmartSearch';
|
||||
import Date from './Date';
|
||||
import CreateIncident from './CreateIncident';
|
||||
|
||||
interface DashboardHeaderProps {
|
||||
fetchIncidentData: (payload: any) => void;
|
||||
startIncidentSearch: () => void;
|
||||
}
|
||||
|
||||
const DashboardHeader: FC<DashboardHeaderProps> = ({ fetchIncidentData }) => {
|
||||
const DashboardHeader: FC<DashboardHeaderProps> = ({
|
||||
fetchIncidentData,
|
||||
startIncidentSearch,
|
||||
}) => {
|
||||
const { title } = DashboardHeaderConstants;
|
||||
const [searchValue, setSearchValue] = useState<string>('');
|
||||
const navigate = useNavigate();
|
||||
@@ -45,12 +50,17 @@ const DashboardHeader: FC<DashboardHeaderProps> = ({ fetchIncidentData }) => {
|
||||
setSearchValue={setSearchValue}
|
||||
updateURLAndFetchData={updateURLAndFetchData}
|
||||
/>
|
||||
<div className={styles['filter']}>
|
||||
<FilterWrapper
|
||||
clearSearchValue={clearSearchValue}
|
||||
fetchIncidentData={fetchIncidentData}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles['filter']}>
|
||||
<FilterWrapper
|
||||
clearSearchValue={clearSearchValue}
|
||||
fetchIncidentData={fetchIncidentData}
|
||||
/>
|
||||
|
||||
<div className={styles['filter']}></div>
|
||||
<div>
|
||||
<CreateIncident startIncidentSearch={startIncidentSearch} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -6,3 +6,7 @@ export interface filterItem {
|
||||
filter_data: any;
|
||||
selection_config: SelectionType;
|
||||
}
|
||||
export interface Team {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
27
src/hooks/useOutsideClick.tsx
Normal file
27
src/hooks/useOutsideClick.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import React from 'react';
|
||||
|
||||
interface useOutsideClickProps {
|
||||
callback: Function;
|
||||
}
|
||||
|
||||
const useOutsideClick = ({ callback }: useOutsideClickProps) => {
|
||||
const ref = React.useRef<HTMLElement>();
|
||||
|
||||
React.useEffect(() => {
|
||||
const handleClick = (event: any) => {
|
||||
if (ref.current && !ref.current.contains(event.target)) {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('click', handleClick, true);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('click', handleClick, true);
|
||||
};
|
||||
}, [ref]);
|
||||
|
||||
return ref;
|
||||
};
|
||||
|
||||
export default useOutsideClick;
|
||||
@@ -18,6 +18,8 @@ const EVENT_NAME = {
|
||||
Houston_Add_Oncall: 'Houston_Add_Oncall',
|
||||
Houston_Add_Pseconcall: 'Houston_Add_Pseconcall',
|
||||
Houston_Create_Team_Initiate: 'Houston_Create_Team_Initiate',
|
||||
Houston_Create_Incident_initiate: 'Houston_Create_Incident_initiate',
|
||||
Houston_Create_Incident_submit: 'Houston_Create_Incident_submit',
|
||||
};
|
||||
|
||||
const SCREEN_NAME = {
|
||||
|
||||
Reference in New Issue
Block a user