Skip to content
This repository was archived by the owner on Sep 3, 2025. It is now read-only.
Merged
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
36 changes: 24 additions & 12 deletions src/dispatch/incident/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
from datetime import datetime
from typing import ForwardRef, List, Optional

from pydantic import validator
from pydantic import validator, Field

from sqlalchemy import Column, DateTime, ForeignKey, Integer, PrimaryKeyConstraint, String, Table
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import relationship
Expand Down Expand Up @@ -46,6 +47,7 @@
from dispatch.report.models import ReportRead
from dispatch.storage.models import StorageRead
from dispatch.tag.models import TagRead, TagReadMinimal
from dispatch.task.enums import TaskStatus
from dispatch.term.models import TermRead
from dispatch.ticket.models import TicketRead
from dispatch.workflow.models import WorkflowInstanceRead
Expand Down Expand Up @@ -235,6 +237,15 @@ class CaseRead(DispatchBase):
name: Optional[NameStr]


class TaskRead(DispatchBase):
id: PrimaryKey
assignees: List[Optional[ParticipantRead]] = []
created_at: Optional[datetime]
description: Optional[str] = Field(None, nullable=True)
status: TaskStatus = TaskStatus.open
weblink: Optional[str]


# Pydantic models...
class IncidentBase(DispatchBase):
title: str
Expand Down Expand Up @@ -327,37 +338,38 @@ def find_exclusive(cls, v):

class IncidentRead(IncidentBase):
id: PrimaryKey
cases: Optional[List[CaseRead]] = []
closed_at: Optional[datetime] = None
commander: Optional[ParticipantRead]
commanders_location: Optional[str]
conference: Optional[ConferenceRead] = None
conversation: Optional[ConversationRead] = None
created_at: Optional[datetime] = None
documents: Optional[List[DocumentRead]] = []
duplicates: Optional[List[IncidentReadMinimal]] = []
duplicates: Optional[List[IncidentReadMinimal]] = []
events: Optional[List[EventRead]] = []
incident_costs: Optional[List[IncidentCostRead]] = []
incident_priority: IncidentPriorityRead
incident_severity: IncidentSeverityRead
incident_type: IncidentTypeRead
last_executive_report: Optional[ReportRead]
last_tactical_report: Optional[ReportRead]
name: Optional[NameStr]
participants: Optional[List[ParticipantRead]] = []
participants_location: Optional[str]
participants_team: Optional[str]
project: ProjectRead
reported_at: Optional[datetime] = None
reporter: Optional[ParticipantRead]
reporters_location: Optional[str]
stable_at: Optional[datetime] = None
tags: Optional[List[TagRead]] = []
total_cost: Optional[float]
cases: Optional[List[CaseRead]] = []
conference: Optional[ConferenceRead] = None
conversation: Optional[ConversationRead] = None
documents: Optional[List[DocumentRead]] = []
duplicates: Optional[List[IncidentReadMinimal]] = []
events: Optional[List[EventRead]] = []
last_executive_report: Optional[ReportRead]
last_tactical_report: Optional[ReportRead]
participants: Optional[List[ParticipantRead]] = []
storage: Optional[StorageRead] = None
tasks: Optional[List[TaskRead]] = []
tags: Optional[List[TagRead]] = []
terms: Optional[List[TermRead]] = []
ticket: Optional[TicketRead] = None
total_cost: Optional[float]
workflow_instances: Optional[List[WorkflowInstanceRead]] = []


Expand Down
10 changes: 10 additions & 0 deletions src/dispatch/static/dispatch/src/filters.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,13 @@ export const activeRoles = function (value) {
}

Vue.filter("activeRoles", activeRoles)

Vue.filter("individualNames", function (value) {
if (value) {
return value
.map(function (item) {
return item.individual.name
})
.join(", ")
}
})
16 changes: 11 additions & 5 deletions src/dispatch/static/dispatch/src/incident/EditSheet.vue
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
<v-tab key="resources"> Resources </v-tab>
<v-tab key="participants"> Participants </v-tab>
<v-tab key="timeline"> Timeline </v-tab>
<v-tab key="tasks"> Tasks </v-tab>
<v-tab key="workflows"> Workflows </v-tab>
<v-tab key="costs"> Costs </v-tab>
</v-tabs>
Expand All @@ -47,6 +48,9 @@
<v-tab-item key="timeline">
<incident-timeline-tab />
</v-tab-item>
<v-tab-item key="tasks">
<incident-tasks-tab />
</v-tab-item>
<v-tab-item key="workflow_instances">
<workflow-instance-tab v-model="workflow_instances" />
</v-tab-item>
Expand All @@ -63,23 +67,25 @@ import { mapFields } from "vuex-map-fields"
import { mapActions } from "vuex"
import { ValidationObserver } from "vee-validate"

import IncidentCostsTab from "@/incident/CostsTab.vue"
import IncidentDetailsTab from "@/incident/DetailsTab.vue"
import IncidentResourcesTab from "@/incident/ResourcesTab.vue"
import IncidentParticipantsTab from "@/incident/ParticipantsTab.vue"
import IncidentResourcesTab from "@/incident/ResourcesTab.vue"
import IncidentTasksTab from "@/incident/TasksTab.vue"
import IncidentTimelineTab from "@/incident/TimelineTab.vue"
import IncidentCostsTab from "@/incident/CostsTab.vue"
import WorkflowInstanceTab from "@/workflow/WorkflowInstanceTab.vue"

export default {
name: "IncidentEditSheet",

components: {
ValidationObserver,
IncidentCostsTab,
IncidentDetailsTab,
IncidentResourcesTab,
IncidentParticipantsTab,
IncidentResourcesTab,
IncidentTasksTab,
IncidentTimelineTab,
IncidentCostsTab,
ValidationObserver,
WorkflowInstanceTab,
},

Expand Down
42 changes: 42 additions & 0 deletions src/dispatch/static/dispatch/src/incident/TasksTab.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<template>
<div>
<div v-if="tasks && tasks.length">
<span v-for="task in tasks" :key="task.id">
<v-list-item :href="task.weblink" target="_blank">
<v-list-item-content>
<v-list-item-title style="width: 200px" class="text-truncate">
{{ task.description }}
</v-list-item-title>
<v-list-item-subtitle>
<strong>Created:</strong> {{ task.created_at | formatRelativeDate }}
| <strong>Status:</strong> {{ task.status }}
| <strong>Assignees:</strong> {{ task.assignees | individualNames }}
</v-list-item-subtitle>
</v-list-item-content>
<v-list-item-action>
<v-list-item-icon>
<v-icon>open_in_new</v-icon>
</v-list-item-icon>
</v-list-item-action>
</v-list-item>
<v-divider />
</span>
</div>
<div v-else>
<p class="text-center">No tasks have been created for this incident.</p>
</div>
</div>
</template>

<script>
import map from "lodash"
import { mapFields } from "vuex-map-fields"

export default {
name: "IncidentTasksTab",

computed: {
...mapFields("incident", ["selected.tasks"]),
},
}
</script>
1 change: 1 addition & 0 deletions src/dispatch/static/dispatch/src/incident/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const getDefaultSelectedState = () => {
status: null,
storage: null,
tags: [],
tasks: [],
terms: [],
ticket: null,
title: null,
Expand Down