From caaded974d5036e2b767e12b5adde0853169f5f6 Mon Sep 17 00:00:00 2001 From: Giran Moodley Date: Wed, 18 Jan 2023 20:07:58 +0000 Subject: [PATCH 1/2] Initial commit of dedicated incident details page --- src/App.js | 2 + .../IncidentDetailsModalComponent.js | 47 ++++++++++++++ .../IncidentDetailsModalComponent.scss | 8 +++ src/redux/incident_details/actions.js | 17 ++++++ src/redux/incident_details/reducers.js | 44 +++++++++++++ src/redux/incident_details/sagas.js | 43 +++++++++++++ src/redux/incident_details/sagas.test.js | 61 +++++++++++++++++++ src/redux/incident_details/selectors.js | 2 + src/redux/persistence/config.js | 1 + src/redux/rootReducer.js | 2 + src/redux/rootSaga.js | 9 +++ 11 files changed, 236 insertions(+) create mode 100644 src/components/IncidentDetailsModal/IncidentDetailsModalComponent.js create mode 100644 src/components/IncidentDetailsModal/IncidentDetailsModalComponent.scss create mode 100644 src/redux/incident_details/actions.js create mode 100644 src/redux/incident_details/reducers.js create mode 100644 src/redux/incident_details/sagas.js create mode 100644 src/redux/incident_details/sagas.test.js create mode 100644 src/redux/incident_details/selectors.js 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..e1e25eb5 --- /dev/null +++ b/src/components/IncidentDetailsModal/IncidentDetailsModalComponent.js @@ -0,0 +1,47 @@ +import { + connect, +} from 'react-redux'; + +import { + Modal, +} from 'react-bootstrap'; + +import { + toggleDisplayIncidentDetailsModal as toggleDisplayIncidentDetailsModalConnected, +} from 'redux/incident_details/actions'; + +import './IncidentDetailsModalComponent.scss'; + +const IncidentDetailsModalComponent = ({ + toggleDisplayIncidentDetailsModal, incidentDetails, +}) => { + const { + displayIncidentDetailsModal, + } = incidentDetails; + + return ( +
+ + + [#99999] Incident Summary Here + + Incident Details Here + +
+ ); +}; + +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/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(), From 1c03b8a6c1a8783704a9405076041b81e6767eba Mon Sep 17 00:00:00 2001 From: Giran Moodley Date: Wed, 18 Jan 2023 22:04:47 +0000 Subject: [PATCH 2/2] Added placeholders for incident details layout --- .../IncidentDetailsModalComponent.js | 44 ++++++++++++++++++- .../subcomponents/AlertsTableComponent.js | 27 ++++++++++++ .../IncidentTimelineComponent.js | 27 ++++++++++++ 3 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 src/components/IncidentDetailsModal/subcomponents/AlertsTableComponent.js create mode 100644 src/components/IncidentDetailsModal/subcomponents/IncidentTimelineComponent.js diff --git a/src/components/IncidentDetailsModal/IncidentDetailsModalComponent.js b/src/components/IncidentDetailsModal/IncidentDetailsModalComponent.js index e1e25eb5..d299be2d 100644 --- a/src/components/IncidentDetailsModal/IncidentDetailsModalComponent.js +++ b/src/components/IncidentDetailsModal/IncidentDetailsModalComponent.js @@ -1,15 +1,25 @@ +import { + useState, +} from 'react'; import { connect, } from 'react-redux'; import { - Modal, + 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 = ({ @@ -18,6 +28,11 @@ const IncidentDetailsModalComponent = ({ const { displayIncidentDetailsModal, } = incidentDetails; + const { + t, + } = useTranslation(); + + const [currentTab, setCurrentTab] = useState('alerts'); return (
@@ -30,7 +45,32 @@ const IncidentDetailsModalComponent = ({ [#99999] Incident Summary Here - Incident Details Here + + + + High Level Details Placeholder + + + Incident Actions Placeholder + + + Incident Fields Placeholder + Notes Placeholder + + + + setCurrentTab(k)}> + + + + + + + + + + +
); 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);