Skip to content

Commit

Permalink
🚀 chore: improve citizen logs (#1309)
Browse files Browse the repository at this point in the history
* 🚀 chore: improve citizen logs

* 🎉 feat: update arrest reports tab
  • Loading branch information
casperiv0 authored Dec 11, 2022
1 parent 3b5f82e commit f74e48d
Show file tree
Hide file tree
Showing 12 changed files with 500 additions and 351 deletions.
Original file line number Diff line number Diff line change
@@ -1,37 +1,21 @@
import { Controller } from "@tsed/di";
import { BadRequest, NotFound } from "@tsed/exceptions";
import { NotFound } from "@tsed/exceptions";
import { UseBeforeEach } from "@tsed/platform-middlewares";
import { QueryParams, BodyParams, PathParams } from "@tsed/platform-params";
import { ContentType, Delete, Description, Get, Post, Put } from "@tsed/schema";
import { userProperties } from "lib/auth/getSessionUser";
import { leoProperties } from "lib/leo/activeOfficer";
import { ContentType, Delete, Description, Get, Put } from "@tsed/schema";
import { prisma } from "lib/prisma";
import { IsAuth } from "middlewares/IsAuth";
import { CREATE_CITIZEN_SCHEMA } from "@snailycad/schemas";
import { validateSchema } from "lib/validateSchema";
import { generateString } from "utils/generateString";
import { citizenInclude } from "controllers/citizen/CitizenController";
import { validateImgurURL } from "utils/image";
import { Prisma, Rank, WhitelistStatus } from "@prisma/client";
import { Prisma, Rank } from "@prisma/client";
import { UsePermissions, Permissions } from "middlewares/UsePermissions";
import {
ACCEPT_DECLINE_TYPES,
type AcceptDeclineType,
} from "controllers/admin/manage/AdminManageUnitsController";
import { isCuid } from "cuid";
import type * as APITypes from "@snailycad/types/api";
import { validateSocialSecurityNumber } from "lib/citizen/validateSSN";

const recordsInclude = {
officer: { include: leoProperties },
violations: {
include: {
penalCode: { include: { warningApplicable: true, warningNotApplicable: true } },
},
},
seizedItems: true,
};

@UseBeforeEach(IsAuth)
@Controller("/admin/manage/citizens")
@ContentType("application/json")
Expand Down Expand Up @@ -84,32 +68,6 @@ export class AdminManageCitizensController {
return { totalCount, citizens };
}

@Get("/records-logs")
@Description("Get all the record logs within the CAD")
@UsePermissions({
fallback: (u) => u.isSupervisor || u.rank !== Rank.USER,
permissions: [
Permissions.ViewCitizens,
Permissions.ManageCitizens,
Permissions.DeleteCitizens,
Permissions.ViewCitizenLogs,
],
})
async getRecordLogsForCitizen(): Promise<APITypes.GetManageRecordLogsData> {
const citizens = await prisma.recordLog.findMany({
orderBy: { createdAt: "desc" },
include: {
warrant: { include: { officer: { include: leoProperties } } },
records: { include: recordsInclude },
citizen: {
include: { user: { select: userProperties }, gender: true, ethnicity: true },
},
},
});

return citizens;
}

@Get("/:id")
@Description(
"Get a citizen by the `id`. Or get all citizens from a user by the `discordId` or `steamId`",
Expand Down Expand Up @@ -141,39 +99,6 @@ export class AdminManageCitizensController {
return citizen;
}

@Post("/records-logs/:id")
@Description("Accept or decline a record by it's id")
@UsePermissions({
fallback: (u) => u.rank !== Rank.USER,
permissions: [Permissions.ManageCitizens, Permissions.ViewCitizenLogs],
})
async acceptOrDeclineArrestReport(
@PathParams("id") id: string,
@BodyParams("type") type: AcceptDeclineType | null,
): Promise<APITypes.PostCitizenRecordLogsData> {
if (!type || !ACCEPT_DECLINE_TYPES.includes(type)) {
throw new BadRequest("invalidType");
}

const record = await prisma.record.findUnique({
where: { id },
});

if (!record) {
throw new NotFound("recordNotFound");
}

const updated = await prisma.record.update({
where: { id: record.id },
data: {
status: type === "ACCEPT" ? WhitelistStatus.ACCEPTED : WhitelistStatus.DECLINED,
},
include: recordsInclude,
});

return updated;
}

@Put("/:id")
@Description("Update a citizen by its id")
@UsePermissions({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import { Controller } from "@tsed/di";
import { UseBeforeEach } from "@tsed/platform-middlewares";
import { QueryParams, PathParams, BodyParams } from "@tsed/platform-params";
import { ContentType, Description, Get, Post } from "@tsed/schema";
import { userProperties } from "lib/auth/getSessionUser";
import { leoProperties } from "lib/leo/activeOfficer";
import { prisma } from "lib/prisma";
import { IsAuth } from "middlewares/IsAuth";
import { Prisma, Rank, RecordType, WhitelistStatus } from "@prisma/client";
import { UsePermissions, Permissions } from "middlewares/UsePermissions";

import type * as APITypes from "@snailycad/types/api";
import { AcceptDeclineType, ACCEPT_DECLINE_TYPES } from "../AdminManageUnitsController";
import { BadRequest, NotFound } from "@tsed/exceptions";

const recordsInclude = {
officer: { include: leoProperties },
violations: {
include: {
penalCode: { include: { warningApplicable: true, warningNotApplicable: true } },
},
},
seizedItems: true,
};

@UseBeforeEach(IsAuth)
@Controller("/admin/manage")
@ContentType("application/json")
export class AdminManageCitizensController {
@Get("/records-logs")
@Description("Get all the record logs within the CAD")
@UsePermissions({
fallback: (u) => u.isSupervisor || u.rank !== Rank.USER,
permissions: [
Permissions.ViewCitizens,
Permissions.ManageCitizens,
Permissions.DeleteCitizens,
Permissions.ViewCitizenLogs,
],
})
async getRecordLogsForCitizen(
@QueryParams("skip", Number) skip = 0,
@QueryParams("includeAll", Boolean) includeAll = false,
@QueryParams("query", String) query = "",
): Promise<APITypes.GetManageRecordLogsData> {
const [name, surname] = query.toString().toLowerCase().split(/ +/g);

const where = {
OR: [
{
name: { contains: name, mode: Prisma.QueryMode.insensitive },
surname: { contains: surname, mode: Prisma.QueryMode.insensitive },
},
{
name: { equals: surname, mode: Prisma.QueryMode.insensitive },
surname: { equals: name, mode: Prisma.QueryMode.insensitive },
},
],
};

const [totalCount, citizens] = await prisma.$transaction([
prisma.citizen.count({
where: { RecordLog: { some: {} }, ...where },
}),
prisma.citizen.findMany({
orderBy: { createdAt: "desc" },
where: { RecordLog: { some: {} }, ...where },
take: includeAll ? undefined : 35,
skip: includeAll ? undefined : skip,
}),
]);

return { citizens, totalCount };
}

@Get("/pending-arrest-reports")
@Description("Get all the record logs within the CAD")
@UsePermissions({
fallback: (u) => u.isSupervisor || u.rank !== Rank.USER,
permissions: [
Permissions.ViewCitizens,
Permissions.ManageCitizens,
Permissions.DeleteCitizens,
Permissions.ViewCitizenLogs,
],
})
async getPendingArrestReports(
@QueryParams("skip", Number) skip = 0,
@QueryParams("includeAll", Boolean) includeAll = false,
): Promise<APITypes.GetManagePendingArrestReports> {
const [totalCount, arrestReports] = await prisma.$transaction([
prisma.recordLog.count({
where: { records: { status: WhitelistStatus.PENDING, type: RecordType.ARREST_REPORT } },
}),
prisma.recordLog.findMany({
orderBy: { createdAt: "desc" },
where: { records: { status: WhitelistStatus.PENDING, type: RecordType.ARREST_REPORT } },
take: includeAll ? undefined : 35,
skip: includeAll ? undefined : skip,
include: {
warrant: { include: { officer: { include: leoProperties } } },
records: { include: recordsInclude },
citizen: {
include: { user: { select: userProperties }, gender: true, ethnicity: true },
},
},
}),
]);

return { arrestReports, totalCount };
}

@Get("/records-logs/:citizenId")
async getCitizenRecordsLogs(
@PathParams("citizenId") citizenId: string,
@QueryParams("skip", Number) skip = 0,
@QueryParams("includeAll", Boolean) includeAll = false,
): Promise<APITypes.GetManageRecordsLogsCitizenData> {
const [totalCount, recordsLogs] = await prisma.$transaction([
prisma.recordLog.count({ where: { citizenId } }),
prisma.recordLog.findMany({
take: includeAll ? undefined : 35,
skip: includeAll ? undefined : skip,
where: { citizenId },
orderBy: { createdAt: "desc" },
include: {
warrant: { include: { officer: { include: leoProperties } } },
records: { include: recordsInclude },
citizen: {
include: { user: { select: userProperties }, gender: true, ethnicity: true },
},
},
}),
]);

return { recordsLogs, totalCount };
}

@Post("/records-logs/:id")
@Description("Accept or decline a record by it's id")
@UsePermissions({
fallback: (u) => u.rank !== Rank.USER,
permissions: [Permissions.ManageCitizens, Permissions.ViewCitizenLogs],
})
async acceptOrDeclineArrestReport(
@PathParams("id") id: string,
@BodyParams("type") type: AcceptDeclineType | null,
): Promise<APITypes.PostCitizenRecordLogsData> {
if (!type || !ACCEPT_DECLINE_TYPES.includes(type)) {
throw new BadRequest("invalidType");
}

const record = await prisma.record.findUnique({
where: { id },
});

if (!record) {
throw new NotFound("recordNotFound");
}

const updated = await prisma.record.update({
where: { id: record.id },
data: {
status: type === "ACCEPT" ? WhitelistStatus.ACCEPTED : WhitelistStatus.DECLINED,
},
include: recordsInclude,
});

return updated;
}
}
3 changes: 3 additions & 0 deletions apps/client/src/components/form/select/PostalSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ interface Props {
postalOptional?: boolean;
addressLabel?: "address" | "location";
postalOnly?: boolean;
isDisabled?: boolean;
}

export function AddressPostalSelect(props: Props) {
Expand Down Expand Up @@ -80,6 +81,7 @@ export function AddressPostalSelect(props: Props) {
<FormRow disabled={props.postalOnly} flexLike>
{props.postalOnly ? null : (
<AsyncListSearchField<AddressValue>
isDisabled={props.isDisabled}
selectedKey={selectedAddress}
allowsCustomValue
defaultItems={address.values}
Expand Down Expand Up @@ -112,6 +114,7 @@ export function AddressPostalSelect(props: Props) {
)}

<AsyncListSearchField<AddressValue>
isDisabled={props.isDisabled}
selectedKey={selectedPostal}
allowsCustomValue
defaultItems={address.values}
Expand Down
Loading

0 comments on commit f74e48d

Please sign in to comment.