From e3f27c17460bb0ee81c703f60ebd855f8c6d9b11 Mon Sep 17 00:00:00 2001 From: Casper <53900565+Dev-CasperTheGhost@users.noreply.github.com> Date: Tue, 25 Jan 2022 16:26:54 +0100 Subject: [PATCH] :tada: feat: able to separate licenses (#333) --- .../migrations/20220125150836_/migration.sql | 5 +++ packages/api/prisma/schema.prisma | 11 ++++- packages/api/src/controllers/admin/Values.ts | 1 + .../src/controllers/admin/values/Import.ts | 2 + packages/client/locales/en/values.json | 1 + .../admin/values/ManageValueModal.tsx | 45 ++++++++++++++++++- .../components/citizen/ManageCitizenForm.tsx | 27 ++++++----- .../citizen/licenses/ManageLicensesModal.tsx | 42 ++++++++++------- .../citizen/vehicles/RegisterVehicleModal.tsx | 8 +++- .../citizen/weapons/RegisterWeaponModal.tsx | 8 +++- .../EditCitizenLicensesModal.tsx | 42 ++++++++++------- packages/client/src/lib/admin/values.ts | 17 ++++++- packages/client/src/lib/utils.ts | 17 ++++++- packages/client/src/types/prisma.ts | 8 ++++ packages/schemas/src/admin/values/import.ts | 2 + 15 files changed, 185 insertions(+), 51 deletions(-) create mode 100644 packages/api/prisma/migrations/20220125150836_/migration.sql diff --git a/packages/api/prisma/migrations/20220125150836_/migration.sql b/packages/api/prisma/migrations/20220125150836_/migration.sql new file mode 100644 index 000000000..1c1feb358 --- /dev/null +++ b/packages/api/prisma/migrations/20220125150836_/migration.sql @@ -0,0 +1,5 @@ +-- CreateEnum +CREATE TYPE "ValueLicenseType" AS ENUM ('LICENSE', 'REGISTRATION_STATUS'); + +-- AlterTable +ALTER TABLE "Value" ADD COLUMN "licenseType" "ValueLicenseType"; diff --git a/packages/api/prisma/schema.prisma b/packages/api/prisma/schema.prisma index a561540a3..c08c6784d 100644 --- a/packages/api/prisma/schema.prisma +++ b/packages/api/prisma/schema.prisma @@ -237,7 +237,7 @@ model Weapon { model MedicalRecord { id String @id @default(cuid()) - user User? @relation(fields: [userId], references: [id], onDelete: Cascade) + user User? @relation(fields: [userId], references: [id], onDelete: Cascade) userId String? citizen Citizen @relation(fields: [citizenId], references: [id], onDelete: Cascade) citizenId String @@ -261,6 +261,10 @@ model Value { updatedAt DateTime @default(now()) @updatedAt position Int? + // LICENSE type specific + // this was added here since the migration of eg `LicenseValue` would be to large. + licenseType ValueLicenseType? + // how to clean this up?! ethnicityToValue Citizen[] @relation("ethnicityToValue") genderToValue Citizen[] @relation("genderToValue") @@ -284,6 +288,11 @@ model Value { MedicalRecord MedicalRecord[] } +enum ValueLicenseType { + LICENSE + REGISTRATION_STATUS +} + model PenalCode { id String @id @default(cuid()) createdAt DateTime @default(now()) diff --git a/packages/api/src/controllers/admin/Values.ts b/packages/api/src/controllers/admin/Values.ts index 53fbc092b..f85266540 100644 --- a/packages/api/src/controllers/admin/Values.ts +++ b/packages/api/src/controllers/admin/Values.ts @@ -424,6 +424,7 @@ export class ValuesController { }, data: { value: body.get("value"), + licenseType: type === "LICENSE" ? body.get("licenseType") : undefined, }, }); diff --git a/packages/api/src/controllers/admin/values/Import.ts b/packages/api/src/controllers/admin/values/Import.ts index 990708009..f82b9db58 100644 --- a/packages/api/src/controllers/admin/values/Import.ts +++ b/packages/api/src/controllers/admin/values/Import.ts @@ -27,6 +27,7 @@ import { EmployeeAsEnum, ShouldDoType, StatusValueType, + ValueLicenseType, } from "@prisma/client"; import { validateSchema } from "lib/validateSchema"; @@ -195,6 +196,7 @@ export const typeHandlers = { isDefault: false, type: type as ValueType, value: item.value, + licenseType: type === "LICENSE" ? (item.licenseType as ValueLicenseType) : undefined, }, }); }), diff --git a/packages/client/locales/en/values.json b/packages/client/locales/en/values.json index b10929375..4ae2cd7e7 100644 --- a/packages/client/locales/en/values.json +++ b/packages/client/locales/en/values.json @@ -13,6 +13,7 @@ "addPenalCodeGroup": "Add Group", "deleteGroup": "Delete Group", "ungrouped": "Ungrouped", + "licenseType": "License Type", "alert_deletePenalCodeGroup": "Are you sure you want to delete {group}? This action cannot be undone.", "alert_deleteValue": "Are you sure you want to delete {value}? This action cannot be undone!" }, diff --git a/packages/client/src/components/admin/values/ManageValueModal.tsx b/packages/client/src/components/admin/values/ManageValueModal.tsx index 4eebc548a..6abcefc0f 100644 --- a/packages/client/src/components/admin/values/ManageValueModal.tsx +++ b/packages/client/src/components/admin/values/ManageValueModal.tsx @@ -21,6 +21,7 @@ import { DriversLicenseCategoryType, EmployeeAsEnum, ShouldDoType, + ValueLicenseType, ValueType, } from "types/prisma"; import { useTranslations } from "use-intl"; @@ -69,13 +70,23 @@ export const DEPARTMENT_LABELS = { [DepartmentType.EMS_FD]: "EMS/FD", }; +export const LICENSE_LABELS = { + [ValueLicenseType.LICENSE]: "License", + [ValueLicenseType.REGISTRATION_STATUS]: "Registration Status", +}; + const SHOULD_DO_VALUES = Object.values(ShouldDoType).map((v) => ({ label: SHOULD_DO_LABELS[v], value: v, })); const DEPARTMENT_TYPES = Object.values(DepartmentType).map((v) => ({ - label: DEPARTMENT_LABELS[v], + label: DEPARTMENT_LABELS[v] as string, + value: v, +})); + +const LICENSE_TYPES = Object.values(ValueLicenseType).map((v) => ({ + label: LICENSE_LABELS[v] as string, value: v, })); @@ -141,6 +152,8 @@ export function ManageValueModal({ onCreate, onUpdate, clType: dlType, type, val type: value?.type ?? "STATUS_CODE", // @ts-expect-error shortcut hash: value?.hash ?? "", + // @ts-expect-error shortcut + licenseType: value?.licenseType ?? null, showPicker: false, }; @@ -186,6 +199,36 @@ export function ManageValueModal({ onCreate, onUpdate, clType: dlType, type, val + {type === "LICENSE" ? ( + + diff --git a/packages/client/src/components/citizen/ManageCitizenForm.tsx b/packages/client/src/components/citizen/ManageCitizenForm.tsx index 4692183ad..6e3e58e6c 100644 --- a/packages/client/src/components/citizen/ManageCitizenForm.tsx +++ b/packages/client/src/components/citizen/ManageCitizenForm.tsx @@ -12,10 +12,11 @@ import { useAuth } from "context/AuthContext"; import { useValues } from "context/ValuesContext"; import { handleValidate } from "lib/handleValidate"; import { Formik, FormikHelpers } from "formik"; -import { Citizen } from "types/prisma"; +import { Citizen, ValueLicenseType } from "types/prisma"; import { useTranslations } from "next-intl"; import { Textarea } from "components/form/Textarea"; import { useFeatureEnabled } from "hooks/useFeatureEnabled"; +import { filterLicenseTypes } from "lib/utils"; interface Props { citizen: Citizen | null; @@ -215,7 +216,7 @@ export function ManageCitizenForm({ ({ - label: v.value, - value: v.id, - }))} + values={filterLicenseTypes(license.values, ValueLicenseType.LICENSE).map( + (v) => ({ + label: v.value, + value: v.id, + }), + )} value={values.weaponLicense} onChange={handleChange} name="weaponLicense" @@ -261,7 +264,7 @@ export function ManageCitizenForm({ ({ - label: v.value, - value: v.id, - }))} + values={filterLicenseTypes(license.values, ValueLicenseType.LICENSE).map( + (v) => ({ + label: v.value, + value: v.id, + }), + )} value={values.ccw} onChange={handleChange} name="ccw" diff --git a/packages/client/src/components/citizen/licenses/ManageLicensesModal.tsx b/packages/client/src/components/citizen/licenses/ManageLicensesModal.tsx index bccbcea8e..c44e5378c 100644 --- a/packages/client/src/components/citizen/licenses/ManageLicensesModal.tsx +++ b/packages/client/src/components/citizen/licenses/ManageLicensesModal.tsx @@ -14,6 +14,8 @@ import { handleValidate } from "lib/handleValidate"; import { useCitizen } from "context/CitizenContext"; import { FormRow } from "components/form/FormRow"; import { useFeatureEnabled } from "hooks/useFeatureEnabled"; +import { filterLicenseTypes } from "lib/utils"; +import { ValueLicenseType } from "types/prisma"; export function ManageLicensesModal() { const { state, execute } = useFetch(); @@ -77,10 +79,12 @@ export function ManageLicensesModal() { ({ - label: license.value, - value: license.id, - }))} + values={filterLicenseTypes(license.values, ValueLicenseType.LICENSE).map( + (license) => ({ + label: license.value, + value: license.id, + }), + )} value={values.pilotLicense} name="pilotLicense" onChange={handleChange} @@ -142,10 +148,12 @@ export function ManageLicensesModal() { <> ({ - label: license.value, - value: license.id, - }))} + values={filterLicenseTypes(license.values, ValueLicenseType.LICENSE).map( + (license) => ({ + label: license.value, + value: license.id, + }), + )} value={values.ccw} name="ccw" onChange={handleChange} diff --git a/packages/client/src/components/citizen/vehicles/RegisterVehicleModal.tsx b/packages/client/src/components/citizen/vehicles/RegisterVehicleModal.tsx index 7edcaeff9..e84138a47 100644 --- a/packages/client/src/components/citizen/vehicles/RegisterVehicleModal.tsx +++ b/packages/client/src/components/citizen/vehicles/RegisterVehicleModal.tsx @@ -10,7 +10,7 @@ import useFetch from "lib/useFetch"; import { useValues } from "src/context/ValuesContext"; import { useModal } from "context/ModalContext"; import { ModalIds } from "types/ModalIds"; -import { Citizen, RegisteredVehicle } from "types/prisma"; +import { Citizen, RegisteredVehicle, ValueLicenseType } from "types/prisma"; import { handleValidate } from "lib/handleValidate"; import { Input } from "components/form/inputs/Input"; import { useCitizen } from "context/CitizenContext"; @@ -19,6 +19,7 @@ import { useAuth } from "context/AuthContext"; import { Toggle } from "components/form/Toggle"; import { useFeatureEnabled } from "hooks/useFeatureEnabled"; import { useBusinessState } from "state/businessState"; +import { filterLicenseTypes } from "lib/utils"; interface Props { vehicle: RegisteredVehicle | null; @@ -172,7 +173,10 @@ export function RegisterVehicleModal({ label={tVehicle("registrationStatus")} > ({ + values={filterLicenseTypes( + license.values, + ValueLicenseType.REGISTRATION_STATUS, + ).map((license) => ({ label: license.value, value: license.id, }))} diff --git a/packages/client/src/components/leo/modals/NameSearchModal/EditCitizenLicensesModal.tsx b/packages/client/src/components/leo/modals/NameSearchModal/EditCitizenLicensesModal.tsx index 9b133acbe..28e7991c4 100644 --- a/packages/client/src/components/leo/modals/NameSearchModal/EditCitizenLicensesModal.tsx +++ b/packages/client/src/components/leo/modals/NameSearchModal/EditCitizenLicensesModal.tsx @@ -8,9 +8,11 @@ import { useValues } from "context/ValuesContext"; import { Form, Formik } from "formik"; import { useFeatureEnabled } from "hooks/useFeatureEnabled"; import useFetch from "lib/useFetch"; +import { filterLicenseTypes } from "lib/utils"; import { useTranslations } from "next-intl"; import { useNameSearch } from "state/nameSearchState"; import { ModalIds } from "types/ModalIds"; +import { ValueLicenseType } from "types/prisma"; export function EditCitizenLicenses() { const common = useTranslations("Common"); @@ -54,10 +56,12 @@ export function EditCitizenLicenses() {
({ - label: license.value, - value: license.id, - }))} + values={filterLicenseTypes(license.values, ValueLicenseType.LICENSE).map( + (license) => ({ + label: license.value, + value: license.id, + }), + )} value={values.pilotLicense} onChange={handleChange} name="pilotLicense" @@ -80,10 +86,12 @@ export function EditCitizenLicenses() { <> ({ - label: license.value, - value: license.id, - }))} + values={filterLicenseTypes(license.values, ValueLicenseType.LICENSE).map( + (license) => ({ + label: license.value, + value: license.id, + }), + )} value={values.ccw} onChange={handleChange} name="ccw" diff --git a/packages/client/src/lib/admin/values.ts b/packages/client/src/lib/admin/values.ts index 5bf242f72..4eb48f676 100644 --- a/packages/client/src/lib/admin/values.ts +++ b/packages/client/src/lib/admin/values.ts @@ -1,4 +1,8 @@ -import { DEPARTMENT_LABELS, SHOULD_DO_LABELS } from "components/admin/values/ManageValueModal"; +import { + DEPARTMENT_LABELS, + LICENSE_LABELS, + SHOULD_DO_LABELS, +} from "components/admin/values/ManageValueModal"; import { useTranslations } from "next-intl"; import { TValue } from "src/pages/admin/values/[path]"; import { @@ -8,6 +12,7 @@ import { DepartmentValue, DivisionValue, VehicleValue, + Value, } from "types/prisma"; const TYPE_LABELS = { @@ -52,6 +57,13 @@ export function useTableDataOfType(type: ValueType) { gameHash: v.hash || common("none"), }; } + case "LICENSE": { + const v = value as Value; + + return { + licenseType: v.licenseType ? LICENSE_LABELS[v.licenseType] : common("none"), + }; + } default: { return {}; } @@ -89,6 +101,9 @@ export function useTableHeadersOfType(type: ValueType) { case "WEAPON": { return [{ Header: t("gameHash"), accessor: "gameHash" }]; } + case "LICENSE": { + return [{ Header: t("licenseType"), accessor: "licenseType" }]; + } default: { return []; } diff --git a/packages/client/src/lib/utils.ts b/packages/client/src/lib/utils.ts index d946e608a..7d20c04d9 100644 --- a/packages/client/src/lib/utils.ts +++ b/packages/client/src/lib/utils.ts @@ -1,7 +1,15 @@ import { useRouter } from "next/router"; import React from "react"; import { FullDeputy, FullOfficer } from "state/dispatchState"; -import { cad as CAD, Citizen, CombinedLeoUnit, Feature, Officer } from "types/prisma"; +import { + cad as CAD, + Citizen, + CombinedLeoUnit, + Feature, + Officer, + Value, + ValueLicenseType, +} from "types/prisma"; import { handleRequest } from "./fetch"; import { IncomingMessage } from "connect"; import type { NextApiRequestCookies } from "next/dist/server/api-utils"; @@ -98,3 +106,10 @@ export function formatDate(date: string | Date | number, options?: { onlyDate: b const hmsString = options?.onlyDate ? "" : "HH:mm:ss"; return format(dateObj, `yyyy-MM-dd ${hmsString}`); } + +export function filterLicenseTypes(licenses: Value<"LICENSE">[], type: ValueLicenseType) { + return licenses.filter((item) => { + if (item.licenseType === null) return true; + return item.licenseType === type; + }); +} diff --git a/packages/client/src/types/prisma.ts b/packages/client/src/types/prisma.ts index 0ae09880d..a14b247a6 100644 --- a/packages/client/src/types/prisma.ts +++ b/packages/client/src/types/prisma.ts @@ -200,6 +200,7 @@ export type Value = { createdAt: Date; updatedAt: Date; position: number | null; + licenseType?: ValueLicenseType | null; }; /** @@ -845,3 +846,10 @@ export const ExpungementRequestStatus = { export type ExpungementRequestStatus = typeof ExpungementRequestStatus[keyof typeof ExpungementRequestStatus]; + +export const ValueLicenseType = { + LICENSE: "LICENSE", + REGISTRATION_STATUS: "REGISTRATION_STATUS", +}; + +export type ValueLicenseType = typeof ValueLicenseType[keyof typeof ValueLicenseType]; diff --git a/packages/schemas/src/admin/values/import.ts b/packages/schemas/src/admin/values/import.ts index fc79f1d01..8d177823b 100644 --- a/packages/schemas/src/admin/values/import.ts +++ b/packages/schemas/src/admin/values/import.ts @@ -1,7 +1,9 @@ import { z } from "zod"; +const LICENSE_TYPE_REGEX = /LICENSE|REGISTRATION_STATUS/; export const BASE_VALUE_SCHEMA = z.object({ value: z.string().min(1).max(255), + licenseType: z.string().max(255).regex(LICENSE_TYPE_REGEX).nullable().optional(), }); export const BASE_ARR = z.array(BASE_VALUE_SCHEMA).min(1);