Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Incident Details Modal #435

Draft
wants to merge 2 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import ReassignModalComponent from 'components/ReassignModal/ReassignModalCompon
import AddResponderModalComponent from 'components/AddResponderModal/AddResponderModalComponent';
import MergeModalComponent from 'components/MergeModal/MergeModalComponent';
import ConfirmQueryModalComponent from 'components/ConfirmQueryModal/ConfirmQueryModalComponent';
import IncidentDetailsModalComponent from 'components/IncidentDetailsModal/IncidentDetailsModalComponent';

import {
refreshIncidentsAsync as refreshIncidentsAsyncConnected,
Expand Down Expand Up @@ -238,6 +239,7 @@ const App = ({
<AddResponderModalComponent />
<MergeModalComponent />
<ConfirmQueryModalComponent />
<IncidentDetailsModalComponent />
</Container>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import {
useState,
} from 'react';
import {
connect,
} from 'react-redux';

import {
Modal, Container, Row, Col, Tabs, Tab,
} from 'react-bootstrap';

import {
useTranslation,
} from 'react-i18next';

import {
toggleDisplayIncidentDetailsModal as toggleDisplayIncidentDetailsModalConnected,
} from 'redux/incident_details/actions';

import AlertsTableComponent from './subcomponents/AlertsTableComponent';
import IncidentTimelineComponent from './subcomponents/IncidentTimelineComponent';

import './IncidentDetailsModalComponent.scss';

const IncidentDetailsModalComponent = ({
toggleDisplayIncidentDetailsModal, incidentDetails,
}) => {
const {
displayIncidentDetailsModal,
} = incidentDetails;
const {
t,
} = useTranslation();

const [currentTab, setCurrentTab] = useState('alerts');

return (
<div className="incident-details-modal-ctr">
<Modal
backdrop="static"
dialogClassName="modal-fullscreen"
show={displayIncidentDetailsModal}
onHide={toggleDisplayIncidentDetailsModal}
>
<Modal.Header closeButton>
<Modal.Title>[#99999] Incident Summary Here</Modal.Title>
</Modal.Header>
<Modal.Body className="modal-body-fullscreen">
<Container fluid>
<Row>
<Col>High Level Details Placeholder</Col>
</Row>
<Row>
<Col>Incident Actions Placeholder</Col>
</Row>
<Row>
<Col>Incident Fields Placeholder</Col>
<Col>Notes Placeholder</Col>
</Row>
<Row>
<Col>
<Tabs activeKey={currentTab} onSelect={(k) => setCurrentTab(k)}>
<Tab eventKey="alerts" title={t('Alerts')}>
<AlertsTableComponent />
</Tab>
<Tab eventKey="timeline" title={t('Timeline')}>
<IncidentTimelineComponent />
</Tab>
</Tabs>
</Col>
</Row>
</Container>
</Modal.Body>
</Modal>
</div>
);
};

const mapStateToProps = (state) => ({
incidentDetails: state.incidentDetails,
});

const mapDispatchToProps = (dispatch) => ({
toggleDisplayIncidentDetailsModal: () => dispatch(toggleDisplayIncidentDetailsModalConnected()),
});

export default connect(mapStateToProps, mapDispatchToProps)(IncidentDetailsModalComponent);
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/* Bootstrap */
.modal-fullscreen {
min-width: 98%;
}

.modal-body-fullscreen {
min-height: 89vh;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {
connect,
} from 'react-redux';

const AlertsTableComponent = ({
incidentDetails,
}) => {
const {
incident,
} = incidentDetails;
if (incident) console.log('hacking eslint for now');
return (
<div>
<p>AlertsTableComponent Placeholder</p>
</div>
);
};

const mapStateToProps = (state) => ({
incidentDetails: state.incidentDetails,
});

const mapDispatchToProps = (dispatch) => ({
placeholder: () => dispatch(() => {}),
});

export default connect(mapStateToProps, mapDispatchToProps)(AlertsTableComponent);
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {
connect,
} from 'react-redux';

const IncidentTimelineComponent = ({
incidentDetails,
}) => {
const {
incident,
} = incidentDetails;
if (incident) console.log('hacking eslint for now');
return (
<div>
<p>IncidentTimelineComponent Placeholder</p>
</div>
);
};

const mapStateToProps = (state) => ({
incidentDetails: state.incidentDetails,
});

const mapDispatchToProps = (dispatch) => ({
placeholder: () => dispatch(() => {}),
});

export default connect(mapStateToProps, mapDispatchToProps)(IncidentTimelineComponent);
17 changes: 17 additions & 0 deletions src/redux/incident_details/actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/* eslint-disable max-len */
// Define Action Types
export const TOGGLE_DISPLAY_INCIDENT_DETAILS_MODAL_REQUESTED = 'TOGGLE_DISPLAY_INCIDENT_DETAILS_MODAL_REQUESTED';
export const TOGGLE_DISPLAY_INCIDENT_DETAILS_MODAL_COMPLETED = 'TOGGLE_DISPLAY_INCIDENT_DETAILS_MODAL_COMPLETED';

export const UPDATE_INCIDENT_DETAILS_MODAL_REQUESTED = 'UPDATE_INCIDENT_DETAILS_MODAL_REQUESTED';
export const UPDATE_INCIDENT_DETAILS_MODAL_COMPLETED = 'UPDATE_INCIDENT_DETAILS_MODAL_COMPLETED';

// Define Actions
export const toggleDisplayIncidentDetailsModal = () => ({
type: TOGGLE_DISPLAY_INCIDENT_DETAILS_MODAL_REQUESTED,
});

export const updateIncidentDetailsModal = (incident) => ({
type: UPDATE_INCIDENT_DETAILS_MODAL_REQUESTED,
incident,
});
44 changes: 44 additions & 0 deletions src/redux/incident_details/reducers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import produce from 'immer';

import {
TOGGLE_DISPLAY_INCIDENT_DETAILS_MODAL_REQUESTED,
TOGGLE_DISPLAY_INCIDENT_DETAILS_MODAL_COMPLETED,
UPDATE_INCIDENT_DETAILS_MODAL_REQUESTED,
UPDATE_INCIDENT_DETAILS_MODAL_COMPLETED,
} from './actions';

const incidentDetails = produce(
(draft, action) => {
switch (action.type) {
case TOGGLE_DISPLAY_INCIDENT_DETAILS_MODAL_REQUESTED:
draft.status = TOGGLE_DISPLAY_INCIDENT_DETAILS_MODAL_REQUESTED;
break;

case TOGGLE_DISPLAY_INCIDENT_DETAILS_MODAL_COMPLETED:
draft.displayIncidentDetailsModal = action.displayIncidentDetailsModal;
draft.status = TOGGLE_DISPLAY_INCIDENT_DETAILS_MODAL_COMPLETED;
break;

case UPDATE_INCIDENT_DETAILS_MODAL_REQUESTED:
draft.status = UPDATE_INCIDENT_DETAILS_MODAL_REQUESTED;
break;

case UPDATE_INCIDENT_DETAILS_MODAL_COMPLETED:
draft.incident = action.incident;
draft.status = UPDATE_INCIDENT_DETAILS_MODAL_COMPLETED;
break;

default:
break;
}
},
{
displayIncidentDetailsModal: true,
incident: null,
status: null,
fetchingData: false,
error: null,
},
);

export default incidentDetails;
43 changes: 43 additions & 0 deletions src/redux/incident_details/sagas.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import {
put, select, takeLatest,
} from 'redux-saga/effects';

import {
TOGGLE_DISPLAY_INCIDENT_DETAILS_MODAL_REQUESTED,
TOGGLE_DISPLAY_INCIDENT_DETAILS_MODAL_COMPLETED,
UPDATE_INCIDENT_DETAILS_MODAL_REQUESTED,
UPDATE_INCIDENT_DETAILS_MODAL_COMPLETED,
} from './actions';

import selectIncidentDetailsData from './selectors';

export function* toggleDisplayIncidentDetailsModal() {
yield takeLatest(
TOGGLE_DISPLAY_INCIDENT_DETAILS_MODAL_REQUESTED,
toggleDisplayIncidentDetailsModalImpl,
);
}

export function* toggleDisplayIncidentDetailsModalImpl() {
const {
displayIncidentDetailsModal,
} = yield select(selectIncidentDetailsData);
yield put({
type: TOGGLE_DISPLAY_INCIDENT_DETAILS_MODAL_COMPLETED,
displayIncidentDetailsModal: !displayIncidentDetailsModal,
});
}

export function* updateIncidentDetailsModal() {
yield takeLatest(UPDATE_INCIDENT_DETAILS_MODAL_REQUESTED, updateIncidentDetailsModalImpl);
}

export function* updateIncidentDetailsModalImpl(action) {
const {
incident,
} = action;
yield put({
type: UPDATE_INCIDENT_DETAILS_MODAL_COMPLETED,
incident,
});
}
61 changes: 61 additions & 0 deletions src/redux/incident_details/sagas.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import {
select,
} from 'redux-saga/effects';
import {
expectSaga,
} from 'redux-saga-test-plan';

import {
TOGGLE_DISPLAY_INCIDENT_DETAILS_MODAL_REQUESTED,
TOGGLE_DISPLAY_INCIDENT_DETAILS_MODAL_COMPLETED,
UPDATE_INCIDENT_DETAILS_MODAL_REQUESTED,
UPDATE_INCIDENT_DETAILS_MODAL_COMPLETED,
} from './actions';
import incidentDetails from './reducers';
import selectIncidentDetails from './selectors';
import {
toggleDisplayIncidentDetailsModal, updateIncidentDetailsModal,
} from './sagas';

describe('Sagas: Incident Details', () => {
it('toggleIncidentDetailsModal', () => {
const expectedDisplayIncidentDetailsModal = true;
return expectSaga(toggleDisplayIncidentDetailsModal)
.withReducer(incidentDetails)
.provide([[select(selectIncidentDetails), { displayIncidentDetailsModal: false }]])
.dispatch({ type: TOGGLE_DISPLAY_INCIDENT_DETAILS_MODAL_REQUESTED })
.put({
type: TOGGLE_DISPLAY_INCIDENT_DETAILS_MODAL_COMPLETED,
displayIncidentDetailsModal: expectedDisplayIncidentDetailsModal,
})
.hasFinalState({
displayIncidentDetailsModal: expectedDisplayIncidentDetailsModal,
incident: null,
status: TOGGLE_DISPLAY_INCIDENT_DETAILS_MODAL_COMPLETED,
fetchingData: false,
error: null,
})
.silentRun();
});
it('updateIncidentDetailsModal', () => {
const incident = {};
return expectSaga(updateIncidentDetailsModal)
.withReducer(incidentDetails)
.dispatch({
type: UPDATE_INCIDENT_DETAILS_MODAL_REQUESTED,
incident,
})
.put({
type: UPDATE_INCIDENT_DETAILS_MODAL_COMPLETED,
incident,
})
.hasFinalState({
displayIncidentDetailsModal: false,
incident,
status: UPDATE_INCIDENT_DETAILS_MODAL_COMPLETED,
fetchingData: false,
error: null,
})
.silentRun();
});
});
2 changes: 2 additions & 0 deletions src/redux/incident_details/selectors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
const selectIncidentDetails = (state) => state.incidentDetails;
export default selectIncidentDetails;
1 change: 1 addition & 0 deletions src/redux/persistence/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const persistConfig = {
'logEntries',
'querySettings',
'incidentActions',
'incidentDetails',
'services',
'teams',
'users',
Expand Down
2 changes: 2 additions & 0 deletions src/redux/rootReducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import logEntries from './log_entries/reducers';
import querySettings from './query_settings/reducers';
import incidentTable from './incident_table/reducers';
import incidentActions from './incident_actions/reducers';
import incidentDetails from './incident_details/reducers';
import services from './services/reducers';
import teams from './teams/reducers';
import priorities from './priorities/reducers';
Expand All @@ -33,6 +34,7 @@ export default combineReducers({
querySettings: persistReducer(querySettingsPersistConfig, querySettings),
incidentTable,
incidentActions,
incidentDetails,
services,
teams,
priorities,
Expand Down
9 changes: 9 additions & 0 deletions src/redux/rootSaga.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ import {
syncWithExternalSystemAsync,
} from './incident_actions/sagas';

import {
toggleDisplayIncidentDetailsModal,
updateIncidentDetailsModal,
} from './incident_details/sagas';

import {
toggleActionAlertsModal, updateActionAlertsModal,
} from './action_alerts/sagas';
Expand Down Expand Up @@ -187,6 +192,10 @@ export default function* rootSaga() {
runCustomIncidentActionAsync(),
syncWithExternalSystemAsync(),

// Incident Details
toggleDisplayIncidentDetailsModal(),
updateIncidentDetailsModal(),

// Action Alerts Modal
toggleActionAlertsModal(),
updateActionAlertsModal(),
Expand Down