diff --git a/apps/api/prisma/migrations/20231122144146_use_updated_at_units/migration.sql b/apps/api/prisma/migrations/20231122144146_use_updated_at_units/migration.sql new file mode 100644 index 000000000..08b9c86b9 --- /dev/null +++ b/apps/api/prisma/migrations/20231122144146_use_updated_at_units/migration.sql @@ -0,0 +1,18 @@ +-- AlterTable +ALTER TABLE "CombinedEmsFdUnit" DROP COLUMN "lastStatusChangeTimestamp", +ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, +ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP; + +-- AlterTable +ALTER TABLE "CombinedLeoUnit" DROP COLUMN "lastStatusChangeTimestamp", +ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, +ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP; + +-- AlterTable +ALTER TABLE "EmsFdDeputy" DROP COLUMN "lastStatusChangeTimestamp"; + +-- AlterTable +ALTER TABLE "MiscCadSettings" DROP COLUMN "inactivityTimeout"; + +-- AlterTable +ALTER TABLE "Officer" DROP COLUMN "lastStatusChangeTimestamp"; diff --git a/apps/api/prisma/schema.prisma b/apps/api/prisma/schema.prisma index f2cd9574b..793858045 100644 --- a/apps/api/prisma/schema.prisma +++ b/apps/api/prisma/schema.prisma @@ -1041,7 +1041,6 @@ model Officer { LeoIncidentInvolvedOfficers LeoIncident[] @relation("involvedOfficers") combinedLeoUnit CombinedLeoUnit? @relation(fields: [combinedLeoUnitId], references: [id]) combinedLeoUnitId String? - lastStatusChangeTimestamp DateTime? qualifications UnitQualification[] IncidentInvolvedUnit IncidentInvolvedUnit[] Note Note[] @@ -1195,57 +1194,59 @@ model IncidentEvent { } model CombinedLeoUnit { - id String @id @default(uuid()) - officers Officer[] - userDefinedCallsign String? - callsign String - callsign2 String? - department DepartmentValue? @relation(fields: [departmentId], references: [id]) - departmentId String? - incremental Int? - radioChannelId String? - status StatusValue? @relation("combinedUnitStatusToValue", fields: [statusId], references: [id]) - statusId String? - pairedUnitTemplate String? - activeCall Call911? @relation("CombinedLeoUnitActiveCall", fields: [activeCallId], references: [id]) - activeCallId String? - activeIncident LeoIncident? @relation("combinedActiveIncident", fields: [activeIncidentId], references: [id]) - activeIncidentId String? - lastStatusChangeTimestamp DateTime? - activeVehicle EmergencyVehicleValue? @relation("activeEmergencyVehicle_combined_unit", fields: [activeVehicleId], references: [id], onDelete: SetNull) - activeVehicleId String? - AssignedUnit AssignedUnit[] - IncidentInvolvedUnit IncidentInvolvedUnit[] - AssignedWarrantOfficer AssignedWarrantOfficer[] - EmsFdIncident EmsFdIncident? @relation(fields: [emsFdIncidentId], references: [id]) - emsFdIncidentId String? - ChatCreator ChatCreator[] + id String @id @default(uuid()) + officers Officer[] + userDefinedCallsign String? + callsign String + callsign2 String? + department DepartmentValue? @relation(fields: [departmentId], references: [id]) + departmentId String? + incremental Int? + radioChannelId String? + status StatusValue? @relation("combinedUnitStatusToValue", fields: [statusId], references: [id]) + statusId String? + pairedUnitTemplate String? + activeCall Call911? @relation("CombinedLeoUnitActiveCall", fields: [activeCallId], references: [id]) + activeCallId String? + activeIncident LeoIncident? @relation("combinedActiveIncident", fields: [activeIncidentId], references: [id]) + activeIncidentId String? + activeVehicle EmergencyVehicleValue? @relation("activeEmergencyVehicle_combined_unit", fields: [activeVehicleId], references: [id], onDelete: SetNull) + activeVehicleId String? + AssignedUnit AssignedUnit[] + IncidentInvolvedUnit IncidentInvolvedUnit[] + AssignedWarrantOfficer AssignedWarrantOfficer[] + EmsFdIncident EmsFdIncident? @relation(fields: [emsFdIncidentId], references: [id]) + emsFdIncidentId String? + ChatCreator ChatCreator[] + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt @@index([statusId]) } model CombinedEmsFdUnit { - id String @id @default(uuid()) - deputies EmsFdDeputy[] - callsign String - userDefinedCallsign String? - callsign2 String? - department DepartmentValue? @relation(fields: [departmentId], references: [id]) - departmentId String? - incremental Int? - radioChannelId String? - status StatusValue? @relation("combinedEmsFdUnitStatusToValue", fields: [statusId], references: [id]) - statusId String? - pairedUnitTemplate String? - activeCall Call911? @relation("CombinedEmsFdUnitActiveCall", fields: [activeCallId], references: [id]) - activeCallId String? - activeIncident LeoIncident? @relation("combinedEmsFdActiveIncident", fields: [activeIncidentId], references: [id]) - activeIncidentId String? - lastStatusChangeTimestamp DateTime? - activeVehicle EmergencyVehicleValue? @relation("activeEmergencyVehicle_combined_emsfd_unit", fields: [activeVehicleId], references: [id], onDelete: SetNull) - activeVehicleId String? - EmsFdIncident EmsFdIncident? @relation(fields: [emsFdIncidentId], references: [id]) - emsFdIncidentId String? + id String @id @default(uuid()) + deputies EmsFdDeputy[] + callsign String + userDefinedCallsign String? + callsign2 String? + department DepartmentValue? @relation(fields: [departmentId], references: [id]) + departmentId String? + incremental Int? + radioChannelId String? + status StatusValue? @relation("combinedEmsFdUnitStatusToValue", fields: [statusId], references: [id]) + statusId String? + pairedUnitTemplate String? + activeCall Call911? @relation("CombinedEmsFdUnitActiveCall", fields: [activeCallId], references: [id]) + activeCallId String? + activeIncident LeoIncident? @relation("combinedEmsFdActiveIncident", fields: [activeIncidentId], references: [id]) + activeIncidentId String? + activeVehicle EmergencyVehicleValue? @relation("activeEmergencyVehicle_combined_emsfd_unit", fields: [activeVehicleId], references: [id], onDelete: SetNull) + activeVehicleId String? + EmsFdIncident EmsFdIncident? @relation(fields: [emsFdIncidentId], references: [id]) + emsFdIncidentId String? + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt AssignedUnit AssignedUnit[] IncidentInvolvedUnit IncidentInvolvedUnit[] @@ -1596,7 +1597,6 @@ model EmsFdDeputy { whitelistStatusId String? logs OfficerLog[] AssignedUnit AssignedUnit[] - lastStatusChangeTimestamp DateTime? qualifications UnitQualification[] IncidentInvolvedUnit IncidentInvolvedUnit[] activeVehicle EmergencyVehicleValue? @relation("activeEmergencyVehicle_emsfd", fields: [activeVehicleId], references: [id], onDelete: SetNull) diff --git a/apps/api/src/controllers/admin/manage/units/manage-units-controller.ts b/apps/api/src/controllers/admin/manage/units/manage-units-controller.ts index 63ad7b38b..a9a599f0a 100644 --- a/apps/api/src/controllers/admin/manage/units/manage-units-controller.ts +++ b/apps/api/src/controllers/admin/manage/units/manage-units-controller.ts @@ -132,8 +132,7 @@ export class AdminManageUnitsController { @QueryParams("days", Number) days = 2, ) { const where = { - lastStatusChangeTimestamp: { - not: null, + updatedAt: { lte: new Date(new Date().getTime() - 1000 * 60 * 60 * 24 * days), }, departmentId, @@ -143,12 +142,12 @@ export class AdminManageUnitsController { prisma.officer.findMany({ where, include: _leoProperties, - orderBy: { lastStatusChangeTimestamp: "desc" }, + orderBy: { updatedAt: "desc" }, }), prisma.emsFdDeputy.findMany({ where, include: unitProperties, - orderBy: { lastStatusChangeTimestamp: "desc" }, + orderBy: { updatedAt: "desc" }, }), ]); @@ -206,7 +205,7 @@ export class AdminManageUnitsController { return prisma[prismaName].deleteMany({ where: { id: unitId, - lastStatusChangeTimestamp: { + updatedAt: { lte: new Date(new Date().getTime() - 1000 * 60 * 60 * 24 * days), }, }, diff --git a/apps/api/src/controllers/citizen/CitizenController.ts b/apps/api/src/controllers/citizen/CitizenController.ts index 9e47dc429..67403bd64 100644 --- a/apps/api/src/controllers/citizen/CitizenController.ts +++ b/apps/api/src/controllers/citizen/CitizenController.ts @@ -42,7 +42,7 @@ export const citizenInclude = Prisma.validator()({ suspendedLicenses: true, licensePoints: true, vehicles: { - orderBy: { createdAt: "desc" }, + orderBy: { createdAt: Prisma.SortOrder.desc }, include: { trimLevels: true, flags: true, @@ -60,7 +60,7 @@ export const citizenInclude = Prisma.validator()({ skip: 0, }, weapons: { - orderBy: { createdAt: "desc" }, + orderBy: { createdAt: Prisma.SortOrder.desc }, take: 12, skip: 0, include: { @@ -68,7 +68,7 @@ export const citizenInclude = Prisma.validator()({ registrationStatus: true, }, }, - medicalRecords: { include: { bloodGroup: true }, orderBy: { createdAt: "desc" } }, + medicalRecords: { include: { bloodGroup: true }, orderBy: { createdAt: Prisma.SortOrder.desc } }, ethnicity: true, gender: true, weaponLicense: true, diff --git a/apps/api/src/controllers/dispatch/dispatch-controller.ts b/apps/api/src/controllers/dispatch/dispatch-controller.ts index c2d0a1f17..79c4b9eed 100644 --- a/apps/api/src/controllers/dispatch/dispatch-controller.ts +++ b/apps/api/src/controllers/dispatch/dispatch-controller.ts @@ -118,7 +118,7 @@ export class DispatchController { }), include: combinedUnitProperties, take: 25, - orderBy: { lastStatusChangeTimestamp: "desc" }, + orderBy: { updatedAt: "desc" }, }); const combinedEmsFdDeputies = await prisma.combinedEmsFdUnit.findMany({ @@ -129,7 +129,7 @@ export class DispatchController { }), include: combinedEmsFdUnitProperties, take: 25, - orderBy: { lastStatusChangeTimestamp: "desc" }, + orderBy: { updatedAt: "desc" }, }); return [...officers, ...deputies, ...combinedOfficers, ...combinedEmsFdDeputies]; diff --git a/apps/api/src/controllers/dispatch/status-controller.ts b/apps/api/src/controllers/dispatch/status-controller.ts index 55944a5c9..0e5cf6210 100644 --- a/apps/api/src/controllers/dispatch/status-controller.ts +++ b/apps/api/src/controllers/dispatch/status-controller.ts @@ -265,7 +265,6 @@ export class StatusController { activeVehicleId: activeEmergencyVehicleId, statusId, incremental, - lastStatusChangeTimestamp: new Date(), }, include: leoProperties, }); @@ -277,7 +276,6 @@ export class StatusController { activeVehicleId: activeEmergencyVehicleId, statusId, incremental, - lastStatusChangeTimestamp: new Date(), }, include: unitProperties, }); @@ -288,7 +286,6 @@ export class StatusController { userDefinedCallsign, activeVehicleId: activeEmergencyVehicleId, statusId, - lastStatusChangeTimestamp: new Date(), }, include: combinedUnitProperties, }); @@ -299,7 +296,6 @@ export class StatusController { userDefinedCallsign, activeVehicleId: activeEmergencyVehicleId, statusId, - lastStatusChangeTimestamp: new Date(), }, include: combinedEmsFdUnitProperties, }); diff --git a/apps/api/src/controllers/ems-fd/ems-fd-controller.ts b/apps/api/src/controllers/ems-fd/ems-fd-controller.ts index aef8c3d99..b8ca8c2c3 100644 --- a/apps/api/src/controllers/ems-fd/ems-fd-controller.ts +++ b/apps/api/src/controllers/ems-fd/ems-fd-controller.ts @@ -212,11 +212,7 @@ export class EmsFdController { @QueryParams("skip", Number) skip = 0, @QueryParams("query", String) query?: string, ): Promise { - const unitsInactivityFilter = getInactivityFilter( - cad, - "unitInactivityTimeout", - "lastStatusChangeTimestamp", - ); + const unitsInactivityFilter = getInactivityFilter(cad, "unitInactivityTimeout"); const activeDispatcher = await prisma.activeDispatchers.findFirst({ where: { userId: user.id }, @@ -239,7 +235,7 @@ export class EmsFdController { }), prisma.combinedEmsFdUnit.findMany({ include: combinedEmsFdUnitProperties, - orderBy: { lastStatusChangeTimestamp: "desc" }, + orderBy: { updatedAt: "desc" }, where: { ...unitsInactivityFilter?.filter, departmentId: activeDispatcher?.departmentId || undefined, diff --git a/apps/api/src/controllers/leo/LeoController.ts b/apps/api/src/controllers/leo/LeoController.ts index 4a3e22a14..55f5424ee 100644 --- a/apps/api/src/controllers/leo/LeoController.ts +++ b/apps/api/src/controllers/leo/LeoController.ts @@ -59,11 +59,7 @@ export class LeoController { @QueryParams("skip", Number) skip = 0, @QueryParams("query", String) query?: string, ): Promise { - const unitsInactivityFilter = getInactivityFilter( - cad, - "unitInactivityTimeout", - "lastStatusChangeTimestamp", - ); + const unitsInactivityFilter = getInactivityFilter(cad, "unitInactivityTimeout"); const activeDispatcher = await prisma.activeDispatchers.findFirst({ where: { userId: user.id }, @@ -86,14 +82,22 @@ export class LeoController { }), prisma.combinedLeoUnit.findMany({ include: combinedUnitProperties, - orderBy: { lastStatusChangeTimestamp: "desc" }, + orderBy: { updatedAt: "desc" }, where: { - ...unitsInactivityFilter?.filter, + status: { NOT: { shouldDo: ShouldDoType.SET_OFF_DUTY } }, + ...(unitsInactivityFilter?.filter ?? {}), + departmentId: activeDispatcher?.departmentId || undefined, }, }), ]); + console.log({ + fl: unitsInactivityFilter?.filter, + officers, + combinedUnits, + }); + return [...combinedUnits, ...officers]; } diff --git a/apps/api/src/lib/get-active-ems-fd-deputy.ts b/apps/api/src/lib/get-active-ems-fd-deputy.ts index a3d6d9d66..b7776938b 100644 --- a/apps/api/src/lib/get-active-ems-fd-deputy.ts +++ b/apps/api/src/lib/get-active-ems-fd-deputy.ts @@ -63,11 +63,7 @@ export async function getActiveDeputy(options: GetActiveDeputyOptions) { }); const cad = await prisma.cad.findFirst({ include: { miscCadSettings: true } }); - const unitsInactivityFilter = getInactivityFilter( - cad!, - "unitInactivityTimeout", - "lastStatusChangeTimestamp", - ); + const unitsInactivityFilter = getInactivityFilter(cad!, "unitInactivityTimeout"); const filters: Prisma.Enumerable = [ { status: { shouldDo: ShouldDoType.SET_OFF_DUTY } }, @@ -76,7 +72,7 @@ export async function getActiveDeputy(options: GetActiveDeputyOptions) { if (unitsInactivityFilter) { filters.push({ - lastStatusChangeTimestamp: { lte: unitsInactivityFilter.lastStatusChangeTimestamp }, + updatedAt: { lte: unitsInactivityFilter.updatedAt }, }); } diff --git a/apps/api/src/lib/leo/activeOfficer.ts b/apps/api/src/lib/leo/activeOfficer.ts index 396baab70..88ee0520d 100644 --- a/apps/api/src/lib/leo/activeOfficer.ts +++ b/apps/api/src/lib/leo/activeOfficer.ts @@ -63,11 +63,7 @@ export async function getActiveOfficer(options: GetActiveOfficerOptions) { }); const cad = await prisma.cad.findFirst({ include: { miscCadSettings: true } }); - const unitsInactivityFilter = getInactivityFilter( - cad!, - "unitInactivityTimeout", - "lastStatusChangeTimestamp", - ); + const unitsInactivityFilter = getInactivityFilter(cad!, "unitInactivityTimeout"); const filters: Prisma.Enumerable = [ { status: { shouldDo: "SET_OFF_DUTY" } }, @@ -76,7 +72,7 @@ export async function getActiveOfficer(options: GetActiveOfficerOptions) { if (unitsInactivityFilter) { filters.push({ - lastStatusChangeTimestamp: { lte: unitsInactivityFilter.lastStatusChangeTimestamp }, + updatedAt: { lte: unitsInactivityFilter.updatedAt }, }); } diff --git a/apps/api/src/lib/leo/setInactiveUnitsOffDuty.ts b/apps/api/src/lib/leo/setInactiveUnitsOffDuty.ts index f311e4b75..66317f37a 100644 --- a/apps/api/src/lib/leo/setInactiveUnitsOffDuty.ts +++ b/apps/api/src/lib/leo/setInactiveUnitsOffDuty.ts @@ -4,11 +4,11 @@ import { prisma } from "lib/data/prisma"; import type { Socket } from "services/socket-service"; import { handleStartEndOfficerLog } from "./handleStartEndOfficerLog"; -export async function setInactiveUnitsOffDuty(lastStatusChangeTimestamp: Date, socket: Socket) { +export async function setInactiveUnitsOffDuty(updatedAt: Date, socket: Socket) { try { const where = { status: { shouldDo: { not: ShouldDoType.SET_OFF_DUTY } }, - lastStatusChangeTimestamp: { lte: lastStatusChangeTimestamp }, + updatedAt: { not: { gte: updatedAt } }, }; const [officers, deputies] = await prisma.$transaction([ @@ -68,14 +68,11 @@ export function filterInactiveUnits( property?: Prop, ): InactivityReturn | null { - const inactivityTimeout = cad.miscCadSettings?.[type] ?? null; + let inactivityTimeoutInMinutes = cad.miscCadSettings?.[type] ?? null; const _prop = property ?? "updatedAt"; - if (!inactivityTimeout) { + if (!inactivityTimeoutInMinutes) { return null; } - const milliseconds = inactivityTimeout * (1000 * 60); + if (inactivityTimeoutInMinutes < 10) { + inactivityTimeoutInMinutes = 10; + } + + const milliseconds = inactivityTimeoutInMinutes * (1000 * 60); const updatedAt = new Date(new Date().getTime() - milliseconds); + console.log({ + updatedAt, + milliseconds, + inactivityTimeoutInMinutes, + }); + const filter = { [_prop]: { gte: updatedAt }, }; diff --git a/apps/api/src/middlewares/handle-inactivity.ts b/apps/api/src/middlewares/handle-inactivity.ts index 1c02d4797..5ebe2d20f 100644 --- a/apps/api/src/middlewares/handle-inactivity.ts +++ b/apps/api/src/middlewares/handle-inactivity.ts @@ -65,16 +65,10 @@ export class HandleInactivity implements MiddlewareMethods { }); } - const unitsInactivityFilter = getInactivityFilter( - cad, - "unitInactivityTimeout", - "lastStatusChangeTimestamp", - ); - + const unitsInactivityFilter = getInactivityFilter(cad, "unitInactivityTimeout"); const dispatcherInactivityTimeout = getInactivityFilter( cad, "activeDispatchersInactivityTimeout", - "updatedAt", ); const incidentInactivityFilter = getInactivityFilter(cad, "incidentInactivityTimeout"); @@ -92,7 +86,7 @@ export class HandleInactivity implements MiddlewareMethods { } if (unitsInactivityFilter) { - await setInactiveUnitsOffDuty(unitsInactivityFilter.lastStatusChangeTimestamp, this.socket); + await setInactiveUnitsOffDuty(unitsInactivityFilter.updatedAt, this.socket); } const boloInactivityTimeout = getInactivityFilter(cad, "boloInactivityTimeout"); diff --git a/apps/api/src/services/socket-service.ts b/apps/api/src/services/socket-service.ts index d4a8ab70c..fa97cd959 100644 --- a/apps/api/src/services/socket-service.ts +++ b/apps/api/src/services/socket-service.ts @@ -112,7 +112,7 @@ export class Socket { include: leoProperties, }), prisma.combinedLeoUnit.findMany({ - orderBy: { lastStatusChangeTimestamp: "desc" }, + orderBy: { updatedAt: "desc" }, include: combinedUnitProperties, }), ]); @@ -136,7 +136,7 @@ export class Socket { include: unitProperties, }), prisma.combinedEmsFdUnit.findMany({ - orderBy: { lastStatusChangeTimestamp: "desc" }, + orderBy: { updatedAt: "desc" }, include: combinedEmsFdUnitProperties, }), ]); diff --git a/apps/client/src/components/admin/manage/cad-settings/misc-features/inactivity-timeout-tab.tsx b/apps/client/src/components/admin/manage/cad-settings/misc-features/inactivity-timeout-tab.tsx index 7aaddc5c9..f5a16fbc8 100644 --- a/apps/client/src/components/admin/manage/cad-settings/misc-features/inactivity-timeout-tab.tsx +++ b/apps/client/src/components/admin/manage/cad-settings/misc-features/inactivity-timeout-tab.tsx @@ -67,6 +67,7 @@ export function InactivityTimeoutTab() { value={values.call911InactivityTimeout} onChange={handleChange} placeholder="120" + min={10} /> @@ -83,6 +84,7 @@ export function InactivityTimeoutTab() { value={values.incidentInactivityTimeout} onChange={handleChange} placeholder="120" + min={10} /> @@ -99,6 +101,7 @@ export function InactivityTimeoutTab() { value={values.unitInactivityTimeout} onChange={handleChange} placeholder="120" + min={10} /> @@ -115,6 +118,7 @@ export function InactivityTimeoutTab() { value={values.activeDispatchersInactivityTimeout} onChange={handleChange} placeholder="120" + min={10} /> @@ -131,6 +135,7 @@ export function InactivityTimeoutTab() { value={values.boloInactivityTimeout} onChange={handleChange} placeholder="120" + min={10} /> @@ -147,6 +152,7 @@ export function InactivityTimeoutTab() { value={values.activeWarrantsInactivityTimeout} onChange={handleChange} placeholder="120" + min={10} /> diff --git a/apps/client/src/components/admin/manage/units/tabs/all-units-tab/prune-units-modal.tsx b/apps/client/src/components/admin/manage/units/tabs/all-units-tab/prune-units-modal.tsx index c63804687..b6678bf6b 100644 --- a/apps/client/src/components/admin/manage/units/tabs/all-units-tab/prune-units-modal.tsx +++ b/apps/client/src/components/admin/manage/units/tabs/all-units-tab/prune-units-modal.tsx @@ -153,10 +153,10 @@ export function PruneUnitsModal() {

{callsign} {unitName}

- {unit.lastStatusChangeTimestamp ? ( + {unit.updatedAt ? (

{t("Management.lastSeen")}:{" "} - {unit.lastStatusChangeTimestamp} + {unit.updatedAt}

) : null}