Skip to content

Commit

Permalink
🎉 implement taxi
Browse files Browse the repository at this point in the history
  • Loading branch information
casperiv0 committed Oct 24, 2021
1 parent b84c963 commit 4d408cc
Show file tree
Hide file tree
Showing 12 changed files with 432 additions and 14 deletions.
21 changes: 21 additions & 0 deletions packages/api/prisma/migrations/20211024071230_/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
-- CreateTable
CREATE TABLE "TaxiCall" (
"id" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"userId" TEXT NOT NULL,
"assignedUnitId" TEXT,
"location" VARCHAR(255) NOT NULL,
"description" TEXT NOT NULL,
"creatorId" TEXT,

CONSTRAINT "TaxiCall_pkey" PRIMARY KEY ("id")
);

-- AddForeignKey
ALTER TABLE "TaxiCall" ADD CONSTRAINT "TaxiCall_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "TaxiCall" ADD CONSTRAINT "TaxiCall_assignedUnitId_fkey" FOREIGN KEY ("assignedUnitId") REFERENCES "Citizen"("id") ON DELETE SET NULL ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "TaxiCall" ADD CONSTRAINT "TaxiCall_creatorId_fkey" FOREIGN KEY ("creatorId") REFERENCES "Citizen"("id") ON DELETE SET NULL ON UPDATE CASCADE;
17 changes: 17 additions & 0 deletions packages/api/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ model User {
Call911 Call911[]
OfficerLog OfficerLog[]
emsFdDeputies EmsFdDeputy[]
TaxiCall TaxiCall[]
}

model Citizen {
Expand Down Expand Up @@ -119,6 +120,8 @@ model Citizen {
warrants Warrant[]
Record Record[]
emsFdDeputies EmsFdDeputy[]
TaxiCall TaxiCall[]
createdTaxiCalls TaxiCall[] @relation("taxiCallCreator")
}

enum Rank {
Expand Down Expand Up @@ -277,6 +280,20 @@ model TowCall {
creatorId String?
}

// taxi
model TaxiCall {
id String @id @default(uuid())
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id])
userId String
assignedUnit Citizen? @relation(fields: [assignedUnitId], references: [id])
assignedUnitId String?
location String @db.VarChar(255)
description String @db.Text
creator Citizen? @relation("taxiCallCreator", fields: [creatorId], references: [id])
creatorId String?
}

// businesses
model Business {
id String @id @default(cuid())
Expand Down
155 changes: 155 additions & 0 deletions packages/api/src/controllers/calls/TaxiController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import { Controller, BodyParams, Context, UseBefore, PathParams } from "@tsed/common";
import { Delete, Get, JsonRequestBody, Post, Put } from "@tsed/schema";
import { prisma } from "../../lib/prisma";
import { validate, TOW_SCHEMA, UPDATE_TOW_SCHEMA } from "@snailycad/schemas";
import { BadRequest, NotFound } from "@tsed/exceptions";
import { IsAuth } from "../../middlewares";
import { Socket } from "../../services/SocketService";

const CITIZEN_SELECTS = {
name: true,
surname: true,
id: true,
};

@Controller("/taxi")
export class TowController {
private socket: Socket;
constructor(socket: Socket) {
this.socket = socket;
}

@Get("/")
async getTaxiCalls() {
const calls = await prisma.taxiCall.findMany({
include: {
assignedUnit: {
select: CITIZEN_SELECTS,
},
creator: {
select: CITIZEN_SELECTS,
},
},
});

return calls;
}

@UseBefore(IsAuth)
@Post("/")
async createTaxiCall(@BodyParams() body: JsonRequestBody, @Context() ctx: Context) {
const error = validate(TOW_SCHEMA, body.toJSON(), true);
if (error) {
throw new BadRequest(error);
}

const citizen = await prisma.citizen.findUnique({
where: {
id: body.get("creatorId"),
},
});

if (!citizen || citizen.userId !== ctx.get("user").id) {
throw new NotFound("notFound");
}

const call = await prisma.taxiCall.create({
data: {
creatorId: body.get("creatorId"),
userId: ctx.get("user").id,
description: body.get("description"),
location: body.get("location"),
},
include: {
assignedUnit: {
select: CITIZEN_SELECTS,
},
creator: {
select: CITIZEN_SELECTS,
},
},
});

this.socket.emitCreateTaxiCall(call);

return call;
}

@UseBefore(IsAuth)
@Put("/:id")
async updateTaxiCall(@PathParams("id") callId: string, @BodyParams() body: JsonRequestBody) {
const error = validate(UPDATE_TOW_SCHEMA, body.toJSON(), true);
if (error) {
throw new BadRequest(error);
}

console.log({ callId });

const call = await prisma.taxiCall.findUnique({
where: {
id: callId,
},
});

if (!call) {
throw new NotFound("notFound");
}

const rawAssignedUnitId = body.get("assignedUnitId");
const assignedUnitId =
rawAssignedUnitId === null
? {
disconnect: true,
}
: body.get("assignedUnitId")
? { connect: { id: body.get("assignedUnitId") } }
: undefined;

const updated = await prisma.taxiCall.update({
where: {
id: callId,
},
data: {
description: body.get("description"),
location: body.get("location"),
assignedUnit: assignedUnitId,
},
include: {
assignedUnit: {
select: CITIZEN_SELECTS,
},
creator: {
select: CITIZEN_SELECTS,
},
},
});

this.socket.emitUpdateTaxiCall(updated);

return updated;
}

@UseBefore(IsAuth)
@Delete("/:id")
async deleteTowCall(@PathParams("id") callId: string) {
const call = await prisma.taxiCall.findUnique({
where: {
id: callId,
},
});

if (!call) {
throw new NotFound("notFound");
}

await prisma.taxiCall.delete({
where: {
id: call.id,
},
});

this.socket.emitDeleteTaxiCall(call);

return true;
}
}
14 changes: 13 additions & 1 deletion packages/api/src/services/SocketService.ts
Original file line number Diff line number Diff line change
@@ -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, Call911Event } from ".prisma/client";
import { Call911, TowCall, Bolo, Call911Event, TaxiCall } from ".prisma/client";

@SocketService("/")
export class Socket {
Expand Down Expand Up @@ -67,4 +67,16 @@ export class Socket {
emitAddCallEvent(event: Call911Event) {
this.io.sockets.emit(SocketEvents.AddCallEvent, event);
}

emitCreateTaxiCall(call: TaxiCall) {
this.io.sockets.emit(SocketEvents.CreateTaxiCall, call);
}

emitUpdateTaxiCall(call: TaxiCall) {
this.io.sockets.emit(SocketEvents.UpdateTaxiCall, call);
}

emitDeleteTaxiCall(call: TaxiCall) {
this.io.sockets.emit(SocketEvents.EndTaxiCall, call);
}
}
2 changes: 2 additions & 0 deletions packages/client/locales/en/calls.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@
"createTaxiCall": "Create taxi call",
"create911Call": "Create 911 call",
"editTowCall": "Edit tow call",
"editTaxiCall": "Edit taxi call",
"assignedUnits": "Assigned Units",
"selectCitizen": "Select citizen",
"endCall": "End Call",
"end911Call": "End 911 Call",
"addEvent": "Add Event",
"selectUnit": "Select Unit",
"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?"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { ModalIds } from "types/ModalIds";
import { TowCall } from "types/prisma";
import { useCitizen } from "context/CitizenContext";
import { FullTowCall } from "src/pages/tow";
import { useRouter } from "next/router";

interface Props {
call: FullTowCall | null;
Expand All @@ -24,6 +25,9 @@ export const AssignToCallModal = ({ call, onSuccess }: Props) => {
const common = useTranslations("Common");
const t = useTranslations("Calls");
const { citizens } = useCitizen();
const router = useRouter();

const isTow = router.pathname === "/tow";

const INITIAL_VALUES = {
assignedUnitId: call?.assignedUnitId ?? "",
Expand All @@ -40,7 +44,8 @@ export const AssignToCallModal = ({ call, onSuccess }: Props) => {
return;
}

const { json } = await execute(`/tow/${call.id}`, {
const path = isTow ? `/tow/${call.id}` : `/taxi/${call.id}`;
const { json } = await execute(path, {
method: "PUT",
data: { ...call, ...values },
});
Expand All @@ -53,7 +58,7 @@ export const AssignToCallModal = ({ call, onSuccess }: Props) => {

return (
<Modal
title="Select Tow Unit"
title={t("selectUnit")}
isOpen={isOpen(ModalIds.AssignToTowCall)}
onClose={() => closeModal(ModalIds.AssignToTowCall)}
className="min-w-[500px]"
Expand Down
25 changes: 20 additions & 5 deletions packages/client/src/components/citizen/tow/ManageTowCall.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,42 @@ import { useModal } from "context/ModalContext";
import { Formik } from "formik";
import { handleValidate } from "lib/handleValidate";
import useFetch from "lib/useFetch";
import { useRouter } from "next/router";
import toast from "react-hot-toast";
import { ModalIds } from "types/ModalIds";
import { TowCall } from "types/prisma";
import { useTranslations } from "use-intl";

interface Props {
call: TowCall | null;
isTow?: boolean;
onUpdate?: (old: TowCall, newC: TowCall) => void;
onDelete?: (call: TowCall) => void;
}

export const ManageTowCallModal = ({ onDelete, onUpdate, call }: Props) => {
export const ManageCallModal = ({ onDelete, onUpdate, isTow: tow, call }: Props) => {
const common = useTranslations("Common");
const t = useTranslations("Calls");
const { isOpen, closeModal, openModal } = useModal();
const { state, execute } = useFetch();
const { citizens } = useCitizen();
const router = useRouter();

const isTowPath = router.pathname === "/tow";
const isTow = typeof tow === "undefined" ? isTowPath : tow;
const title = isTow
? call
? t("editTowCall")
: t("createTowCall")
: call
? t("editTaxiCall")
: t("createTaxiCall");

async function handleEndCall() {
if (!call) return;

const { json } = await execute(`/tow/${call.id}`, {
const path = isTow ? `/tow/${call.id}` : `/taxi/${call.id}`;
const { json } = await execute(path, {
method: "DELETE",
});

Expand All @@ -47,7 +61,8 @@ export const ManageTowCallModal = ({ onDelete, onUpdate, call }: Props) => {

async function onSubmit(values: typeof INITIAL_VALUES) {
if (call) {
const { json } = await execute(`/tow/${call.id}`, {
const path = isTow ? `/tow/${call.id}` : `/taxi/${call.id}`;
const { json } = await execute(path, {
method: "PUT",
data: { ...call, ...values, assignedUnitId: (call as any).assignedUnit?.id ?? "" },
});
Expand All @@ -56,7 +71,7 @@ export const ManageTowCallModal = ({ onDelete, onUpdate, call }: Props) => {
onUpdate?.(call, json);
}
} else {
const { json } = await execute("/tow", {
const { json } = await execute(isTow ? "/tow" : "/taxi", {
method: "POST",
data: values,
});
Expand All @@ -81,7 +96,7 @@ export const ManageTowCallModal = ({ onDelete, onUpdate, call }: Props) => {
return (
<Modal
onClose={() => closeModal(ModalIds.ManageTowCall)}
title={call ? t("editTowCall") : t("createTowCall")}
title={title}
isOpen={isOpen(ModalIds.ManageTowCall)}
className="min-w-[700px]"
>
Expand Down
Loading

0 comments on commit 4d408cc

Please sign in to comment.