diff --git a/packages/api/src/controllers/dispatch/Calls911Controller.ts b/packages/api/src/controllers/dispatch/Calls911Controller.ts index b5fb6397d..ab368ff43 100644 --- a/packages/api/src/controllers/dispatch/Calls911Controller.ts +++ b/packages/api/src/controllers/dispatch/Calls911Controller.ts @@ -171,7 +171,7 @@ export class Calls911Controller { @UseBefore(IsDispatch) @Delete("/:id") - async end911Call(@PathParams("id") id: string, @BodyParams() body: JsonRequestBody) { + async end911Call(@PathParams("id") id: string) { const call = await prisma.call911.findUnique({ where: { id }, }); @@ -180,21 +180,12 @@ export class Calls911Controller { throw new NotFound("callNotFound"); } - const event = await prisma.call911Event.create({ - data: { - call911Id: call.id, - description: body.get("description"), - }, - }); - - // todo: update sockets - - return event; + return true; } @UseBefore(IsDispatch) @Post("/events/:callId") - async createCallEvent(@PathParams("callId") callId: string) { + async createCallEvent(@PathParams("callId") callId: string, @BodyParams() body: JsonRequestBody) { const call = await prisma.call911.findUnique({ where: { id: callId }, }); @@ -203,6 +194,15 @@ export class Calls911Controller { throw new NotFound("callNotFound"); } - return true; + const event = await prisma.call911Event.create({ + data: { + call911Id: call.id, + description: body.get("description"), + }, + }); + + this.socket.emitAddCallEvent(event); + + return event; } } diff --git a/packages/api/src/services/SocketService.ts b/packages/api/src/services/SocketService.ts index 195f00138..ff1ee8049 100644 --- a/packages/api/src/services/SocketService.ts +++ b/packages/api/src/services/SocketService.ts @@ -1,7 +1,7 @@ import { Nsp, SocketService } from "@tsed/socketio"; import * as SocketIO from "socket.io"; import { SocketEvents } from "@snailycad/config"; -import { Call911, TowCall, Bolo } from ".prisma/client"; +import { Call911, TowCall, Bolo, Call911Event } from ".prisma/client"; @SocketService("/") export class Socket { @@ -63,4 +63,8 @@ export class Socket { emitUserBanned(userId: string) { this.io.sockets.emit(SocketEvents.UserBanned, userId); } + + emitAddCallEvent(event: Call911Event) { + this.io.sockets.emit(SocketEvents.AddCallEvent, event); + } } diff --git a/packages/client/locales/en/calls.json b/packages/client/locales/en/calls.json index 908bf5ded..a339a25a7 100644 --- a/packages/client/locales/en/calls.json +++ b/packages/client/locales/en/calls.json @@ -12,11 +12,14 @@ "caller": "Caller", "createTowCall": "Create tow call", "createTaxiCall": "Create taxi call", + "create911Call": "Create 911 call", "editTowCall": "Edit tow call", "assignedUnits": "Assigned Units", "selectCitizen": "Select citizen", "endCall": "End Call", "end911Call": "End 911 Call", + "addEvent": "Add Event", + "noEvents": "This call does not have any events", "alert_end911Call": "Are you sure you want to end this call?", "alert_endTowCall": "Are you sure you want to end this call?" } diff --git a/packages/client/locales/en/common.json b/packages/client/locales/en/common.json index 7c615ddbd..dc495d4d4 100644 --- a/packages/client/locales/en/common.json +++ b/packages/client/locales/en/common.json @@ -23,7 +23,8 @@ "decline": "Decline", "createdAt": "Created At", "reset": "Reset", - "search": "Search" + "search": "Search", + "events": "Events" }, "Errors": { "unknown": "An unexpected error occurred", diff --git a/packages/client/src/components/dispatch/ModalButtons.tsx b/packages/client/src/components/dispatch/ModalButtons.tsx index 4738c3e53..849b12a7f 100644 --- a/packages/client/src/components/dispatch/ModalButtons.tsx +++ b/packages/client/src/components/dispatch/ModalButtons.tsx @@ -4,51 +4,67 @@ import { ModalIds } from "types/ModalIds"; import { useModal } from "context/ModalContext"; interface MButton { - nameKey: string; + nameKey: [string, string]; modalId: string; } const buttons: MButton[] = [ { - nameKey: "nameSearch", + nameKey: ["Leo", "nameSearch"], modalId: ModalIds.NameSearch, }, { - nameKey: "plateSearch", + nameKey: ["Leo", "plateSearch"], modalId: ModalIds.VehicleSearch, }, { - nameKey: "weaponSearch", + nameKey: ["Leo", "weaponSearch"], modalId: ModalIds.WeaponSearch, }, { - nameKey: "addressSearch", - modalId: ModalIds.AddressSearch, + nameKey: ["Leo", "createWrittenWarning"], + modalId: ModalIds.CreateWrittenWarning, }, { - nameKey: "createBolo", + nameKey: ["Leo", "createTicket"], + modalId: ModalIds.CreateTicket, + }, + { + nameKey: ["Leo", "createArrestReport"], + modalId: ModalIds.CreateArrestReport, + }, + { + nameKey: ["Leo", "createBolo"], modalId: ModalIds.ManageBolo, }, { - nameKey: "notepad", + nameKey: ["Calls", "create911Call"], + modalId: ModalIds.Manage911Call, + }, + { + nameKey: ["Leo", "notepad"], modalId: ModalIds.Notepad, }, + { + nameKey: ["Leo", "activeOfficers"], + modalId: ModalIds.ActiveOfficers, + }, ]; export const DispatchModalButtons = () => { const { openModal } = useModal(); - const t = useTranslations("Leo"); + const t = useTranslations(); return ( diff --git a/packages/client/src/components/leo/ModalButtons.tsx b/packages/client/src/components/leo/ModalButtons.tsx index 1e7446108..54e195c82 100644 --- a/packages/client/src/components/leo/ModalButtons.tsx +++ b/packages/client/src/components/leo/ModalButtons.tsx @@ -6,45 +6,45 @@ import { useModal } from "context/ModalContext"; import { useTranslations } from "use-intl"; interface MButton { - nameKey: string; + nameKey: [string, string]; modalId: string; } const buttons: MButton[] = [ { - nameKey: "nameSearch", + nameKey: ["Leo", "nameSearch"], modalId: ModalIds.NameSearch, }, { - nameKey: "plateSearch", + nameKey: ["Leo", "plateSearch"], modalId: ModalIds.VehicleSearch, }, { - nameKey: "weaponSearch", + nameKey: ["Leo", "weaponSearch"], modalId: ModalIds.WeaponSearch, }, { - nameKey: "createWrittenWarning", + nameKey: ["Leo", "createWrittenWarning"], modalId: ModalIds.CreateWrittenWarning, }, { - nameKey: "createTicket", + nameKey: ["Leo", "createTicket"], modalId: ModalIds.CreateTicket, }, { - nameKey: "createArrestReport", + nameKey: ["Leo", "createArrestReport"], modalId: ModalIds.CreateArrestReport, }, { - nameKey: "createBolo", + nameKey: ["Leo", "createBolo"], modalId: ModalIds.ManageBolo, }, { - nameKey: "notepad", + nameKey: ["Leo", "notepad"], modalId: ModalIds.Notepad, }, { - nameKey: "activeOfficers", + nameKey: ["Leo", "activeOfficers"], modalId: ModalIds.ActiveOfficers, }, ]; @@ -52,7 +52,7 @@ const buttons: MButton[] = [ export const ModalButtons = () => { const { activeOfficer } = useLeoState(); const { openModal } = useModal(); - const t = useTranslations("Leo"); + const t = useTranslations(); const isButtonDisabled = !activeOfficer || @@ -71,13 +71,13 @@ export const ModalButtons = () => { diff --git a/packages/client/src/components/modals/Manage911CallModal.tsx b/packages/client/src/components/modals/Manage911CallModal.tsx index 126d4aca8..004ffb719 100644 --- a/packages/client/src/components/modals/Manage911CallModal.tsx +++ b/packages/client/src/components/modals/Manage911CallModal.tsx @@ -3,7 +3,7 @@ import { Button } from "components/Button"; import { Modal } from "components/modal/Modal"; import { useModal } from "context/ModalContext"; import { ModalIds } from "types/ModalIds"; -import { Form, Formik } from "formik"; +import { Form, Formik, FormikHelpers } from "formik"; import { Input } from "components/form/Input"; import { FormField } from "components/form/FormField"; import { Error } from "components/form/Error"; @@ -13,8 +13,12 @@ import { Loader } from "components/Loader"; import { Full911Call, useDispatchState } from "state/dispatchState"; import { useRouter } from "next/router"; import { useAuth } from "context/AuthContext"; +import format from "date-fns/format"; import { Select, SelectValue } from "components/form/Select"; import { AlertModal } from "components/modal/AlertModal"; +import compareDesc from "date-fns/compareDesc"; +import { useListener } from "@casper124578/use-socket.io"; +import { SocketEvents } from "@snailycad/config"; interface Props { call: Full911Call | null; @@ -32,6 +36,26 @@ export const Manage911CallModal = ({ call, onClose }: Props) => { const isDispatch = router.pathname === "/dispatch" && user?.isDispatch; const { allOfficers } = useDispatchState(); + useListener( + SocketEvents.AddCallEvent, + (event) => { + if (!call) return; + + call.events.push(event); + + setCalls( + calls.map((c) => { + if (c.id === call.id) { + return { ...c, events: [event, ...c.events] }; + } + + return c; + }), + ); + }, + [call, calls, setCalls], + ); + function handleClose() { onClose?.(); closeModal(ModalIds.Manage911Call); @@ -50,6 +74,17 @@ export const Manage911CallModal = ({ call, onClose }: Props) => { } } + async function onEventSubmit(values: { description: string }, helpers: FormikHelpers) { + if (!call) return; + + await execute(`/911-calls/events/${call.id}`, { + method: "POST", + data: values, + }); + + helpers.resetForm(); + } + async function onSubmit(values: typeof INITIAL_VALUES) { if (call) { const { json } = await execute(`/911-calls/${call.id}`, { @@ -103,74 +138,122 @@ export const Manage911CallModal = ({ call, onClose }: Props) => { isOpen={isOpen(ModalIds.Manage911Call)} onClose={handleClose} title={"Manage 911 Call"} - className="min-w-[600px]" + className={call ? "min-w-[850px]" : "min-w-[650px]"} > - - {({ handleChange, values, errors }) => ( -
- - - {errors.name} - - - - - {errors.location} - - - -