diff --git a/src/App.js b/src/App.js
index b3efaffc..aeb86978 100644
--- a/src/App.js
+++ b/src/App.js
@@ -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,
@@ -238,6 +239,7 @@ const App = ({
+
);
diff --git a/src/components/IncidentDetailsModal/IncidentDetailsModalComponent.js b/src/components/IncidentDetailsModal/IncidentDetailsModalComponent.js
new file mode 100644
index 00000000..d299be2d
--- /dev/null
+++ b/src/components/IncidentDetailsModal/IncidentDetailsModalComponent.js
@@ -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 (
+
+
+
+ [#99999] Incident Summary Here
+
+
+
+
+ High Level Details Placeholder
+
+
+ Incident Actions Placeholder
+
+
+ Incident Fields Placeholder
+ Notes Placeholder
+
+
+
+ setCurrentTab(k)}>
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const mapStateToProps = (state) => ({
+ incidentDetails: state.incidentDetails,
+});
+
+const mapDispatchToProps = (dispatch) => ({
+ toggleDisplayIncidentDetailsModal: () => dispatch(toggleDisplayIncidentDetailsModalConnected()),
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(IncidentDetailsModalComponent);
diff --git a/src/components/IncidentDetailsModal/IncidentDetailsModalComponent.scss b/src/components/IncidentDetailsModal/IncidentDetailsModalComponent.scss
new file mode 100644
index 00000000..95931145
--- /dev/null
+++ b/src/components/IncidentDetailsModal/IncidentDetailsModalComponent.scss
@@ -0,0 +1,8 @@
+/* Bootstrap */
+.modal-fullscreen {
+ min-width: 98%;
+}
+
+.modal-body-fullscreen {
+ min-height: 89vh;
+}
diff --git a/src/components/IncidentDetailsModal/subcomponents/AlertsTableComponent.js b/src/components/IncidentDetailsModal/subcomponents/AlertsTableComponent.js
new file mode 100644
index 00000000..c322ab45
--- /dev/null
+++ b/src/components/IncidentDetailsModal/subcomponents/AlertsTableComponent.js
@@ -0,0 +1,27 @@
+import {
+ connect,
+} from 'react-redux';
+
+const AlertsTableComponent = ({
+ incidentDetails,
+}) => {
+ const {
+ incident,
+ } = incidentDetails;
+ if (incident) console.log('hacking eslint for now');
+ return (
+
+
AlertsTableComponent Placeholder
+
+ );
+};
+
+const mapStateToProps = (state) => ({
+ incidentDetails: state.incidentDetails,
+});
+
+const mapDispatchToProps = (dispatch) => ({
+ placeholder: () => dispatch(() => {}),
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(AlertsTableComponent);
diff --git a/src/components/IncidentDetailsModal/subcomponents/IncidentTimelineComponent.js b/src/components/IncidentDetailsModal/subcomponents/IncidentTimelineComponent.js
new file mode 100644
index 00000000..dd0b0934
--- /dev/null
+++ b/src/components/IncidentDetailsModal/subcomponents/IncidentTimelineComponent.js
@@ -0,0 +1,27 @@
+import {
+ connect,
+} from 'react-redux';
+
+const IncidentTimelineComponent = ({
+ incidentDetails,
+}) => {
+ const {
+ incident,
+ } = incidentDetails;
+ if (incident) console.log('hacking eslint for now');
+ return (
+
+
IncidentTimelineComponent Placeholder
+
+ );
+};
+
+const mapStateToProps = (state) => ({
+ incidentDetails: state.incidentDetails,
+});
+
+const mapDispatchToProps = (dispatch) => ({
+ placeholder: () => dispatch(() => {}),
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(IncidentTimelineComponent);
diff --git a/src/redux/incident_details/actions.js b/src/redux/incident_details/actions.js
new file mode 100644
index 00000000..adea13a2
--- /dev/null
+++ b/src/redux/incident_details/actions.js
@@ -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,
+});
diff --git a/src/redux/incident_details/reducers.js b/src/redux/incident_details/reducers.js
new file mode 100644
index 00000000..c587a46a
--- /dev/null
+++ b/src/redux/incident_details/reducers.js
@@ -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;
diff --git a/src/redux/incident_details/sagas.js b/src/redux/incident_details/sagas.js
new file mode 100644
index 00000000..2bb79824
--- /dev/null
+++ b/src/redux/incident_details/sagas.js
@@ -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,
+ });
+}
diff --git a/src/redux/incident_details/sagas.test.js b/src/redux/incident_details/sagas.test.js
new file mode 100644
index 00000000..f1676344
--- /dev/null
+++ b/src/redux/incident_details/sagas.test.js
@@ -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();
+ });
+});
diff --git a/src/redux/incident_details/selectors.js b/src/redux/incident_details/selectors.js
new file mode 100644
index 00000000..994a4f64
--- /dev/null
+++ b/src/redux/incident_details/selectors.js
@@ -0,0 +1,2 @@
+const selectIncidentDetails = (state) => state.incidentDetails;
+export default selectIncidentDetails;
diff --git a/src/redux/persistence/config.js b/src/redux/persistence/config.js
index 0410288d..10163fc6 100644
--- a/src/redux/persistence/config.js
+++ b/src/redux/persistence/config.js
@@ -9,6 +9,7 @@ export const persistConfig = {
'logEntries',
'querySettings',
'incidentActions',
+ 'incidentDetails',
'services',
'teams',
'users',
diff --git a/src/redux/rootReducer.js b/src/redux/rootReducer.js
index 38a390e0..28b56550 100644
--- a/src/redux/rootReducer.js
+++ b/src/redux/rootReducer.js
@@ -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';
@@ -33,6 +34,7 @@ export default combineReducers({
querySettings: persistReducer(querySettingsPersistConfig, querySettings),
incidentTable,
incidentActions,
+ incidentDetails,
services,
teams,
priorities,
diff --git a/src/redux/rootSaga.js b/src/redux/rootSaga.js
index 16400ed9..e9124aea 100644
--- a/src/redux/rootSaga.js
+++ b/src/redux/rootSaga.js
@@ -71,6 +71,11 @@ import {
syncWithExternalSystemAsync,
} from './incident_actions/sagas';
+import {
+ toggleDisplayIncidentDetailsModal,
+ updateIncidentDetailsModal,
+} from './incident_details/sagas';
+
import {
toggleActionAlertsModal, updateActionAlertsModal,
} from './action_alerts/sagas';
@@ -187,6 +192,10 @@ export default function* rootSaga() {
runCustomIncidentActionAsync(),
syncWithExternalSystemAsync(),
+ // Incident Details
+ toggleDisplayIncidentDetailsModal(),
+ updateIncidentDetailsModal(),
+
// Action Alerts Modal
toggleActionAlertsModal(),
updateActionAlertsModal(),