diff --git a/apps/api/src/controllers/leo/incidents/IncidentController.ts b/apps/api/src/controllers/leo/incidents/IncidentController.ts
index d0fb52266..281821295 100644
--- a/apps/api/src/controllers/leo/incidents/IncidentController.ts
+++ b/apps/api/src/controllers/leo/incidents/IncidentController.ts
@@ -168,6 +168,7 @@ export class IncidentController {
}
@Post("/:type/:incidentId")
+ @Description("Assign or unassign a unit from an Active Incident")
@UsePermissions({
permissions: [Permissions.Dispatch, Permissions.Leo, Permissions.EmsFd],
})
diff --git a/apps/client/locales/en/leo.json b/apps/client/locales/en/leo.json
index b4ff5142e..428fe38fe 100644
--- a/apps/client/locales/en/leo.json
+++ b/apps/client/locales/en/leo.json
@@ -353,7 +353,9 @@
"OTHER": "Other",
"RADAR": "Radar",
"LASER": "Laser",
- "PACE": "Pace"
+ "PACE": "Pace",
+ "unassignFromIncident": "Unassign from incident",
+ "assignToIncident": "Assign to incident"
},
"Bolos": {
"activeBolos": "Active Bolos",
@@ -385,4 +387,4 @@
"bureauOfFirearms": "Bureau of Firearms",
"noWeaponsPendingBof": "There are no weapons pending approval."
}
-}
+}
\ No newline at end of file
diff --git a/apps/client/src/components/dispatch/active-calls/columns/actions-column.tsx b/apps/client/src/components/dispatch/active-calls/columns/actions-column.tsx
index c792e924d..0e4f3398c 100644
--- a/apps/client/src/components/dispatch/active-calls/columns/actions-column.tsx
+++ b/apps/client/src/components/dispatch/active-calls/columns/actions-column.tsx
@@ -32,9 +32,7 @@ export function ActiveCallsActionsColumn({
const { hasActiveDispatchers } = useActiveDispatchers();
const { hasPermissions } = usePermission();
const router = useRouter();
- const { setCurrentlySelectedCall } = useCall911State((s) => ({
- setCurrentlySelectedCall: s.setCurrentlySelectedCall,
- }));
+ const setCurrentlySelectedCall = useCall911State((s) => s.setCurrentlySelectedCall);
const t = useTranslations("Calls");
const common = useTranslations("Common");
diff --git a/apps/client/src/components/dispatch/active-incidents/active-incidents.tsx b/apps/client/src/components/dispatch/active-incidents/active-incidents.tsx
index f71436d3c..02e48edc9 100644
--- a/apps/client/src/components/dispatch/active-incidents/active-incidents.tsx
+++ b/apps/client/src/components/dispatch/active-incidents/active-incidents.tsx
@@ -20,6 +20,12 @@ import { CallDescription } from "../active-calls/CallDescription";
import dynamic from "next/dynamic";
import { useActiveIncidents } from "hooks/realtime/useActiveIncidents";
import compareDesc from "date-fns/compareDesc";
+import { usePermission } from "hooks/usePermission";
+import { defaultPermissions } from "@snailycad/permissions";
+import { useRouter } from "next/router";
+import { useEmsFdState } from "state/ems-fd-state";
+import { useLeoState } from "state/leo-state";
+import { ActiveIncidentsActionsColumn } from "./columns/actions-column";
const ManageIncidentModal = dynamic(
async () => (await import("components/leo/incidents/manage-incident-modal")).ManageIncidentModal,
@@ -40,7 +46,21 @@ export function ActiveIncidents() {
const draggingUnit = useDispatchState((state) => state.draggingUnit);
const asyncTable = useActiveIncidentsTable();
+ const router = useRouter();
const { activeIncidents } = useActiveIncidents();
+ const { hasPermissions } = usePermission();
+
+ const activeDeputy = useEmsFdState((state) => state.activeDeputy);
+ const activeOfficer = useLeoState((state) => state.activeOfficer);
+
+ const hasDispatchPermissions = hasPermissions(defaultPermissions.defaultDispatchPermissions);
+ const isDispatch = router.pathname === "/dispatch" && hasDispatchPermissions;
+ const activeUnitForRoute =
+ router.pathname === "/officer"
+ ? activeOfficer
+ : router.pathname === "/ems-fd"
+ ? activeDeputy
+ : null;
const tableState = useTableState({
tableId: "active-incidents",
@@ -84,16 +104,6 @@ export function ActiveIncidents() {
}
}
- function onEditClick(incident: LeoIncident) {
- modalState.openModal(ModalIds.ManageIncident);
- setTempIncident(incident);
- }
-
- function onEndClick(incident: LeoIncident) {
- modalState.openModal(ModalIds.AlertDeleteIncident);
- setTempIncident(incident);
- }
-
function handleCreateIncident() {
modalState.openModal(ModalIds.ManageIncident);
setTempIncident("create");
@@ -104,16 +114,18 @@ export function ActiveIncidents() {
{asyncTable.noItemsAvailable ? (
@@ -127,11 +139,19 @@ export function ActiveIncidents() {
data={activeIncidents
.sort((a, b) => compareDesc(new Date(a.updatedAt), new Date(b.updatedAt)))
.map((incident) => {
+ const isUnitAssigned = incident.unitsInvolved.some(
+ (v) => v.unit?.id === activeUnitForRoute?.id,
+ );
+
return {
+ rowProps: {
+ className: classNames(isUnitAssigned && "bg-gray-200 dark:bg-amber-900"),
+ },
id: incident.id,
caseNumber: `#${incident.caseNumber}`,
unitsInvolved: (
@@ -143,26 +163,13 @@ export function ActiveIncidents() {
situationCode: incident.situationCode?.value.value ?? common("none"),
description: ,
actions: (
- <>
-
-
-
- >
+
),
};
})}
@@ -180,24 +187,26 @@ export function ActiveIncidents() {
/>
)}
-
- onDrop={({ incident, unit }) => {
- if (!unit.unit?.id) return;
- handleAssignUnassignToIncident(incident, unit.unit.id, "unassign");
- }}
- accepts={[DndActions.UnassignUnitFromIncident]}
- >
-
+ onDrop={({ incident, unit }) => {
+ if (!unit.unit?.id) return;
+ handleAssignUnassignToIncident(incident, unit.unit.id, "unassign");
+ }}
+ accepts={[DndActions.UnassignUnitFromIncident]}
>
-
{t("dropToUnassignFromIncident")}
-
-
+
+
{t("dropToUnassignFromIncident")}
+
+
+ ) : null}
{tempIncident === "hide" ? null : (
+
+
+ {isDispatch ? (
+
+ ) : (
+
+ )}
+ >
+ );
+}
diff --git a/apps/client/src/components/dispatch/active-incidents/columns/involved-units-column.tsx b/apps/client/src/components/dispatch/active-incidents/columns/involved-units-column.tsx
index c5c79a83c..e6e8c2a8c 100644
--- a/apps/client/src/components/dispatch/active-incidents/columns/involved-units-column.tsx
+++ b/apps/client/src/components/dispatch/active-incidents/columns/involved-units-column.tsx
@@ -17,6 +17,7 @@ import { DndActions } from "types/dnd-actions";
interface Props {
incident: LeoIncident;
+ isDispatch: boolean;
handleAssignUnassignToIncident(
incident: LeoIncident,
unitId: string,
@@ -24,14 +25,18 @@ interface Props {
): Promise;
}
-export function InvolvedUnitsColumn({ handleAssignUnassignToIncident, incident }: Props) {
+export function InvolvedUnitsColumn({
+ isDispatch,
+ handleAssignUnassignToIncident,
+ incident,
+}: Props) {
const common = useTranslations("Common");
const setDraggingUnit = useDispatchState((state) => state.setDraggingUnit);
const { generateCallsign } = useGenerateCallsign();
const { hasActiveDispatchers } = useActiveDispatchers();
- const canDrag = hasActiveDispatchers;
+ const canDrag = isDispatch && hasActiveDispatchers;
function makeAssignedUnit(unit: IncidentInvolvedUnit) {
if (!unit.unit) return "UNKNOWN";
@@ -54,6 +59,7 @@ export function InvolvedUnitsColumn({ handleAssignUnassignToIncident, incident }
{
+ if (!canDrag) return;
setDraggingUnit(isDragging ? "incident" : null);
}}
key={unit.id}
diff --git a/apps/client/src/components/leo/incidents/incidents-table.tsx b/apps/client/src/components/leo/incidents/incidents-table.tsx
index 5f3c87f80..a60cec2db 100644
--- a/apps/client/src/components/leo/incidents/incidents-table.tsx
+++ b/apps/client/src/components/leo/incidents/incidents-table.tsx
@@ -173,7 +173,7 @@ export function IncidentsTable(
injuriesOrFatalities: common(yesOrNoText(incident.injuriesOrFatalities)),
arrestsMade: common(yesOrNoText(incident.arrestsMade)),
situationCode: incident.situationCode?.value.value ?? common("none"),
- description: ,
+ description: ,
createdAt: {incident.createdAt},
actions: (
<>
diff --git a/apps/client/src/components/leo/incidents/manage-incident-modal.tsx b/apps/client/src/components/leo/incidents/manage-incident-modal.tsx
index e67c5d8c6..3610c22d7 100644
--- a/apps/client/src/components/leo/incidents/manage-incident-modal.tsx
+++ b/apps/client/src/components/leo/incidents/manage-incident-modal.tsx
@@ -63,7 +63,7 @@ function areFormFieldsDisabled(options: AreFormFieldsDisabledOptions) {
const isAssignedToIncident = options.incident.unitsInvolved.some(
(u) => u.unit?.id === options.activeUnit?.id,
);
- return !isAssignedToIncident;
+ return isAssignedToIncident;
}
// todo: make this an optional feature
@@ -99,11 +99,15 @@ export function ManageIncidentModal({
const isDispatch = router.pathname.includes("/dispatch");
const isLeoIncidents = type === "leo";
- const isEmsFdIncidents = router.pathname.includes("/ems-fd");
- const isOfficerIncidents = router.pathname.includes("/officer");
+ const isEmsFdIncidentsPage = router.pathname === "/ems-fd/incidents";
+ const isOfficerIncidentsPage = router.pathname === "/officer/incidents";
- const activeUnit = isOfficerIncidents ? activeOfficer : isEmsFdIncidents ? activeDeputy : null;
- const isReadOnly = isOfficerIncidents || isEmsFdIncidents;
+ const activeUnit = isOfficerIncidentsPage
+ ? activeOfficer
+ : isEmsFdIncidentsPage
+ ? activeDeputy
+ : null;
+ const isReadOnly = isOfficerIncidentsPage || isEmsFdIncidentsPage;
const areFieldsDisabled = areFormFieldsDisabled({
isActiveIncidentsList: !isReadOnly,
@@ -298,7 +302,11 @@ export function ManageIncidentModal({
{incident ? (
-
+
) : null}
@@ -332,7 +340,7 @@ export function ManageIncidentModal({
{incident ? (
) : null}
diff --git a/apps/client/src/components/shared/utility-panel/edit-dashboard-layout-modal.tsx b/apps/client/src/components/shared/utility-panel/edit-dashboard-layout-modal.tsx
index 42929a3fb..66f173f5f 100644
--- a/apps/client/src/components/shared/utility-panel/edit-dashboard-layout-modal.tsx
+++ b/apps/client/src/components/shared/utility-panel/edit-dashboard-layout-modal.tsx
@@ -24,6 +24,7 @@ const cardTypes: Record<"ems-fd" | "officer" | "dispatch", DashboardLayoutCardTy
DashboardLayoutCardType.ACTIVE_WARRANTS,
DashboardLayoutCardType.ACTIVE_OFFICERS,
DashboardLayoutCardType.ACTIVE_DEPUTIES,
+ DashboardLayoutCardType.ACTIVE_INCIDENTS,
],
dispatch: [
DashboardLayoutCardType.ACTIVE_OFFICERS,
diff --git a/apps/client/src/pages/officer/index.tsx b/apps/client/src/pages/officer/index.tsx
index 2b35f4b1f..0850f4b7f 100644
--- a/apps/client/src/pages/officer/index.tsx
+++ b/apps/client/src/pages/officer/index.tsx
@@ -45,6 +45,7 @@ import { CreateWarrantModal } from "components/leo/modals/CreateWarrantModal";
import { useCall911State } from "state/dispatch/call-911-state";
import { usePermission } from "hooks/usePermission";
import { useAuth } from "context/AuthContext";
+import { ActiveIncidents } from "components/dispatch/active-incidents/active-incidents";
const Modals = {
CreateWarrantModal: dynamic(
@@ -154,7 +155,7 @@ export default function OfficerDashboard({
}));
const set911Calls = useCall911State((state) => state.setCalls);
const t = useTranslations("Leo");
- const { ACTIVE_WARRANTS, CALLS_911 } = useFeatureEnabled();
+ const { ACTIVE_INCIDENTS, ACTIVE_WARRANTS, CALLS_911 } = useFeatureEnabled();
const { user } = useAuth();
const session = user ?? _session;
@@ -196,6 +197,11 @@ export default function OfficerDashboard({
isEnabled: true,
children: ,
},
+ {
+ type: DashboardLayoutCardType.ACTIVE_INCIDENTS,
+ isEnabled: ACTIVE_INCIDENTS,
+ children: ,
+ },
];
const layoutOrder = session?.officerLayoutOrder ?? [];