Skip to content
This repository was archived by the owner on Sep 3, 2025. It is now read-only.

Commit 8a7cc38

Browse files
authored
Allows to handoff incidents in bulk (#2984)
* Adds dialog to handoff incidents in the Web UI * Removes commented code * Improvements * Bugfixes * Improvements * New dialog version * Improvements * Removes Pydantic model * Removes incident view * removes bulkHandoff api call * removes commented code * makes eslint happy
1 parent b701156 commit 8a7cc38

File tree

9 files changed

+132
-37
lines changed

9 files changed

+132
-37
lines changed

src/dispatch/incident/views.py

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ def get_incidents(
7070
expand: bool = Query(default=False),
7171
):
7272
"""Retrieves a list of incidents."""
73-
print(expand)
7473
pagination = search_filter_sort_paginate(model="Incident", **common)
7574

7675
if expand:
@@ -175,6 +174,22 @@ def update_incident(
175174
return incident
176175

177176

177+
@router.delete(
178+
"/{incident_id}",
179+
response_model=None,
180+
summary="Delete an incident.",
181+
dependencies=[Depends(PermissionsDependency([IncidentEditPermission]))],
182+
)
183+
def delete_incident(
184+
*,
185+
incident_id: PrimaryKey,
186+
db_session: Session = Depends(get_db),
187+
current_incident: Incident = Depends(get_current_incident),
188+
):
189+
"""Deletes an incident."""
190+
delete(db_session=db_session, incident_id=current_incident.id)
191+
192+
178193
@router.post(
179194
"/{incident_id}/join",
180195
summary="Adds an individual to an incident.",
@@ -271,22 +286,6 @@ def create_executive_report(
271286
)
272287

273288

274-
@router.delete(
275-
"/{incident_id}",
276-
response_model=None,
277-
summary="Delete an incident.",
278-
dependencies=[Depends(PermissionsDependency([IncidentEditPermission]))],
279-
)
280-
def delete_incident(
281-
*,
282-
incident_id: PrimaryKey,
283-
db_session: Session = Depends(get_db),
284-
current_incident: Incident = Depends(get_current_incident),
285-
):
286-
"""Deletes an incident."""
287-
delete(db_session=db_session, incident_id=current_incident.id)
288-
289-
290289
def get_month_range(relative):
291290
today = date.today()
292291
relative_month = today - relativedelta(months=relative)

src/dispatch/models.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
from typing import Optional
22
from datetime import datetime, timedelta
33

4-
import validators
5-
4+
from pydantic import BaseModel
65
from pydantic.fields import Field
76
from pydantic.networks import EmailStr
8-
from pydantic import BaseModel, validator
97
from pydantic.types import conint, constr, SecretStr
108

9+
from sqlalchemy import Boolean, Column, DateTime, Integer, String, event, ForeignKey
1110
from sqlalchemy import func
12-
from sqlalchemy.orm import relationship
13-
from sqlalchemy.ext.hybrid import hybrid_property
1411
from sqlalchemy.ext.declarative import declared_attr
15-
from sqlalchemy import Boolean, Column, DateTime, Integer, String, event, ForeignKey
12+
from sqlalchemy.ext.hybrid import hybrid_property
13+
from sqlalchemy.orm import relationship
1614

1715
# pydantic type that limits the range of primary keys
1816
PrimaryKey = conint(gt=0, lt=2147483647)

src/dispatch/static/dispatch/src/api.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,16 @@ instance.interceptors.response.use(
102102
}
103103

104104
if (err.response.status == 500) {
105-
let errorText = err.response.data.detail.map(({ msg }) => msg).join(" ")
105+
let errorText = ""
106+
if (err.response.data.detail) {
107+
errorText = err.response.data.detail.map(({ msg }) => msg).join(" ")
108+
}
109+
106110
if (errorText.length == 0) {
107111
errorText =
108-
"Something has gone wrong, please retry or let your admin know that you received this error."
112+
"Something has gone wrong. Please, retry or let your admin know that you received this error."
109113
}
114+
110115
store.commit(
111116
"notification_backend/addBeNotification",
112117
{

src/dispatch/static/dispatch/src/case/DetailsTab.vue

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,10 @@
111111
</template>
112112

113113
<script>
114-
import { mapFields } from "vuex-map-fields"
115114
import { ValidationProvider, extend } from "vee-validate"
115+
import { mapFields } from "vuex-map-fields"
116116
import { required } from "vee-validate/dist/rules"
117+
117118
import CaseFilterCombobox from "@/case/CaseFilterCombobox.vue"
118119
import CasePrioritySelect from "@/case/priority/CasePrioritySelect.vue"
119120
import CaseSeveritySelect from "@/case/severity/CaseSeveritySelect.vue"
@@ -123,12 +124,15 @@ import IncidentFilterCombobox from "@/incident/IncidentFilterCombobox.vue"
123124
import ParticipantSelect from "@/incident/ParticipantSelect.vue"
124125
import ProjectSelect from "@/project/ProjectSelect.vue"
125126
import TagFilterAutoComplete from "@/tag/TagFilterAutoComplete.vue"
127+
126128
extend("required", {
127129
...required,
128130
message: "This field is required",
129131
})
132+
130133
export default {
131134
name: "CaseDetailsTab",
135+
132136
components: {
133137
CaseFilterCombobox,
134138
CasePrioritySelect,
@@ -141,12 +145,14 @@ export default {
141145
TagFilterAutoComplete,
142146
ValidationProvider,
143147
},
148+
144149
data() {
145150
return {
146151
statuses: ["New", "Triage", "Escalated", "Closed"],
147152
visibilities: ["Open", "Restricted"],
148153
}
149154
},
155+
150156
computed: {
151157
...mapFields("case_management", [
152158
"selected.assignee",
@@ -160,11 +166,11 @@ export default {
160166
"selected.id",
161167
"selected.incidents",
162168
"selected.name",
163-
"selected.signals",
164169
"selected.project",
165170
"selected.related",
166171
"selected.reported_at",
167172
"selected.resolution",
173+
"selected.signals",
168174
"selected.status",
169175
"selected.tags",
170176
"selected.title",

src/dispatch/static/dispatch/src/incident/BulkEditSheet.vue

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
<template>
22
<v-bottom-sheet v-model="showBulkEdit" hide-overlay persistent>
3+
<handoff-dialog />
34
<v-card :loading="bulkEditLoading" tile>
45
<v-list>
56
<v-list-item>
67
<v-list-item-content>
78
<v-list-item-subtitle>{{ selected.length }} selected</v-list-item-subtitle>
89
</v-list-item-content>
910
<v-spacer />
11+
<v-list-item-icon>
12+
<v-btn text @click="showHandoffDialog()">
13+
<v-icon>mdi-account-arrow-right</v-icon>
14+
Handoff
15+
</v-btn>
16+
</v-list-item-icon>
1017
<v-list-item-icon>
1118
<v-btn text @click="saveBulk({ status: 'Active' })">
1219
<v-icon>mdi-check</v-icon>
@@ -40,16 +47,26 @@
4047
<script>
4148
import { mapFields } from "vuex-map-fields"
4249
import { mapActions } from "vuex"
50+
51+
import HandoffDialog from "@/incident/HandoffDialog.vue"
52+
4353
export default {
4454
name: "IncidentBulkEditSheet",
55+
56+
components: {
57+
HandoffDialog,
58+
},
59+
4560
computed: {
4661
...mapFields("incident", ["table.rows.selected", "table.bulkEditLoading"]),
62+
4763
showBulkEdit: function () {
4864
return this.selected.length ? true : false
4965
},
5066
},
67+
5168
methods: {
52-
...mapActions("incident", ["saveBulk", "deleteBulk"]),
69+
...mapActions("incident", ["saveBulk", "deleteBulk", "showHandoffDialog"]),
5370
},
5471
}
5572
</script>
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<template>
2+
<v-dialog v-model="showHandoffDialog" persistent max-width="800px">
3+
<v-card>
4+
<v-card-title>
5+
<span class="headline">Handoff Incidents</span>
6+
</v-card-title>
7+
<v-card-text>
8+
Select the new commander for the selected incidents.
9+
</v-card-text>
10+
<v-card-actions>
11+
<v-container grid-list-md>
12+
<v-layout wrap>
13+
<v-flex xs12>
14+
<participant-select v-model="commander" label="Incident Commander" :project="project"/>
15+
</v-flex>
16+
<!-- <v-flex xs12> -->
17+
<!-- <v-checkbox v-model="report" label="Generate Report"/> -->
18+
<!-- </v-flex> -->
19+
<v-btn color="blue en-1" text @click="closeHandoffDialog()"> Cancel </v-btn>
20+
<v-btn color="red en-1" text :loading="loading" @click="saveBulk({commander: commander})">
21+
Handoff
22+
</v-btn>
23+
</v-layout>
24+
</v-container>
25+
</v-card-actions>
26+
</v-card>
27+
</v-dialog>
28+
</template>
29+
30+
<script>
31+
import { mapFields } from "vuex-map-fields"
32+
import { mapActions } from "vuex"
33+
34+
import ParticipantSelect from "@/incident/ParticipantSelect.vue"
35+
36+
export default {
37+
name: "IncidentHandoffDialog",
38+
39+
data() {
40+
return {
41+
commander: { individual: { name: "Commander Name" }},
42+
report: false,
43+
}
44+
},
45+
46+
components: {
47+
ParticipantSelect,
48+
},
49+
50+
computed: {
51+
...mapFields("incident", ["dialogs.showHandoffDialog", "selected.loading", "selected.project"]),
52+
},
53+
54+
methods: {
55+
...mapActions("incident", ["closeHandoffDialog", "saveBulk", "resetSelected"]),
56+
},
57+
}
58+
</script>

src/dispatch/static/dispatch/src/incident/Table.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,11 +129,11 @@ import IncidentPriority from "@/incident/priority/IncidentPriority.vue"
129129
import IncidentSeverity from "@/incident/severity/IncidentSeverity.vue"
130130
import IncidentStatus from "@/incident/status/IncidentStatus.vue"
131131
import NewSheet from "@/incident/NewSheet.vue"
132-
import WorkflowRunModal from "@/workflow/RunModal.vue"
133132
import ReportDialog from "@/incident/ReportDialog.vue"
134133
import RouterUtils from "@/router/utils"
135134
import TableExportDialog from "@/incident/TableExportDialog.vue"
136135
import TableFilterDialog from "@/incident/TableFilterDialog.vue"
136+
import WorkflowRunModal from "@/workflow/RunModal.vue"
137137
138138
export default {
139139
name: "IncidentTable",
@@ -148,9 +148,9 @@ export default {
148148
IncidentStatus,
149149
NewSheet,
150150
ReportDialog,
151-
WorkflowRunModal,
152151
TableExportDialog,
153152
TableFilterDialog,
153+
WorkflowRunModal,
154154
},
155155
156156
props: {

src/dispatch/static/dispatch/src/incident/api.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,6 @@ export default {
2727
return API.put(`/${resource}/${incidentId}`, payload)
2828
},
2929

30-
createReport(incidentId, type, payload) {
31-
return API.post(`/${resource}/${incidentId}/report/${type}`, payload)
32-
},
33-
3430
bulkUpdate(incidents, payload) {
3531
return Promise.all(
3632
incidents.map((incident) => {
@@ -39,6 +35,10 @@ export default {
3935
)
4036
},
4137

38+
delete(incidentId) {
39+
return API.delete(`/${resource}/${incidentId}`)
40+
},
41+
4242
bulkDelete(incidents) {
4343
return Promise.all(
4444
incidents.map((incident) => {
@@ -55,7 +55,7 @@ export default {
5555
return API.post(`/${resource}/${incidentId}/subscribe`, payload)
5656
},
5757

58-
delete(incidentId) {
59-
return API.delete(`/${resource}/${incidentId}`)
58+
createReport(incidentId, type, payload) {
59+
return API.post(`/${resource}/${incidentId}/report/${type}`, payload)
6060
},
6161
}

src/dispatch/static/dispatch/src/incident/store.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,11 @@ const state = {
6262
},
6363
dialogs: {
6464
showDeleteDialog: false,
65-
showReportDialog: false,
6665
showEditSheet: false,
6766
showExport: false,
67+
showHandoffDialog: false,
6868
showNewSheet: false,
69+
showReportDialog: false,
6970
},
7071
report: {
7172
...getDefaultReportState(),
@@ -214,6 +215,14 @@ const actions = {
214215
closeExport({ commit }) {
215216
commit("SET_DIALOG_SHOW_EXPORT", false)
216217
},
218+
showHandoffDialog({ commit }, value) {
219+
commit("SET_DIALOG_SHOW_HANDOFF", true)
220+
commit("SET_SELECTED", value)
221+
},
222+
closeHandoffDialog({ commit }) {
223+
commit("SET_DIALOG_SHOW_HANDOFF", false)
224+
commit("RESET_SELECTED")
225+
},
217226
report({ commit, dispatch }) {
218227
commit("SET_SELECTED_LOADING", true)
219228
return IncidentApi.create(state.selected)
@@ -381,6 +390,9 @@ const mutations = {
381390
SET_DIALOG_SHOW_EXPORT(state, value) {
382391
state.dialogs.showExport = value
383392
},
393+
SET_DIALOG_SHOW_HANDOFF(state, value) {
394+
state.dialogs.showHandoffDialog = value
395+
},
384396
SET_DIALOG_DELETE(state, value) {
385397
state.dialogs.showDeleteDialog = value
386398
},

0 commit comments

Comments
 (0)