Skip to content

Commit

Permalink
🎉 feat: address dropdowns (#1202)
Browse files Browse the repository at this point in the history
* 🎉 feat: address dropdowns

* chore: add types

* chore: fix types

* 🎉 feat: working import & crud

* Format code with prettier

* 🎉 feat: working AddressPostalSelect

* Format code with prettier

* 🎉 feat: bug fixes & improvements

* 🎉 feat: add all instances of postal/address search

* Format code with prettier

* 🔨 chore: 1 migration

Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com>
  • Loading branch information
casperiv0 and deepsource-autofix[bot] authored Nov 1, 2022
1 parent 4e156e0 commit 1c16a6f
Show file tree
Hide file tree
Showing 32 changed files with 310 additions and 134 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
-- AlterEnum
ALTER TYPE "ValueType" ADD VALUE 'ADDRESS';

-- CreateTable
CREATE TABLE "AddressValue" (
"id" TEXT NOT NULL,
"valueId" TEXT NOT NULL,
"county" TEXT,
"postal" TEXT,

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

-- AddForeignKey
ALTER TABLE "AddressValue" ADD CONSTRAINT "AddressValue_valueId_fkey" FOREIGN KEY ("valueId") REFERENCES "Value"("id") ON DELETE CASCADE ON UPDATE CASCADE;
10 changes: 10 additions & 0 deletions apps/api/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,15 @@ model Value {
CallTypeValue CallTypeValue[] @relation("callTypeValueToValue")
WeaponExam WeaponExam[] @relation("weaponExamToLicense")
LicenseExam LicenseExam[] @relation("examToLicense")
AddressValue AddressValue[] @relation("addressValueToValue")
}

model AddressValue {
id String @id @default(uuid())
value Value @relation("addressValueToValue", fields: [valueId], references: [id], onDelete: Cascade)
valueId String
county String?
postal String?
}

model PenalCode {
Expand Down Expand Up @@ -1440,6 +1449,7 @@ enum ValueType {
CITIZEN_FLAG
QUALIFICATION
CALL_TYPE
ADDRESS
}

enum DriversLicenseCategoryType {
Expand Down
19 changes: 19 additions & 0 deletions apps/api/src/controllers/admin/values/Import.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
PENAL_CODE_ARR,
QUALIFICATION_ARR,
CALL_TYPE_ARR,
ADDRESS_SCHEMA_ARR,
} from "@snailycad/schemas";
import {
type DepartmentType,
Expand Down Expand Up @@ -103,6 +104,24 @@ interface HandlerOptions {
}

export const typeHandlers = {
ADDRESS: async ({ body, id }: HandlerOptions) => {
const data = validateSchema(ADDRESS_SCHEMA_ARR, body);

return prisma.$transaction(
data.map((item) => {
return prisma.addressValue.upsert({
where: { id: String(id) },
...makePrismaData(ValueType.ADDRESS, {
postal: item.postal,
county: item.county,
value: item.value,
isDisabled: item.isDisabled,
}),
include: { value: true },
});
}),
);
},
VEHICLE: async ({ body, id }: HandlerOptions) => {
const data = validateSchema(HASH_SCHEMA_ARR, body);

Expand Down
20 changes: 18 additions & 2 deletions apps/api/src/controllers/admin/values/ValuesController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const GET_VALUES: Partial<Record<ValueType, ValuesSelect>> = {
include: { department: { include: { value: true } } },
},
CALL_TYPE: { name: "callTypeValue" },
ADDRESS: { name: "addressValue" },
};

@Controller("/admin/values/:path")
Expand Down Expand Up @@ -78,7 +79,7 @@ export class ValuesController {
values: await prisma[data.name].findMany({
include: {
...(data.include ?? {}),
_count: true,
...(type === "ADDRESS" ? {} : { _count: true }),
value: true,
},
orderBy: { value: { position: "asc" } },
Expand Down Expand Up @@ -127,11 +128,26 @@ export class ValuesController {
const data = GET_VALUES[type];

if (data) {
let where: any = {
value: { isDisabled: false, value: { contains: query, mode: "insensitive" } },
};

if (ValueType.ADDRESS === type) {
where = {
OR: [
{ value: { value: { contains: query, mode: "insensitive" } } },
{ county: { contains: query, mode: "insensitive" } },
{ postal: { contains: query, mode: "insensitive" } },
],
AND: [{ value: { isDisabled: false } }],
};
}

// @ts-expect-error ignore
const values = await prisma[data.name].findMany({
include: { ...(data.include ?? {}), value: true },
orderBy: { value: { position: "asc" } },
where: { value: { isDisabled: false, value: { contains: query, mode: "insensitive" } } },
where,
});

return values;
Expand Down
4 changes: 3 additions & 1 deletion apps/api/src/lib/values/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@ export type ValuesSelect =
| ({ name: "departmentValue" } & Prisma.DepartmentValueFindManyArgs)
| ({ name: "divisionValue" } & Prisma.DivisionValueFindManyArgs)
| ({ name: "qualificationValue" } & Prisma.QualificationValueFindManyArgs)
| ({ name: "callTypeValue" } & Prisma.CallTypeValueFindManyArgs);
| ({ name: "callTypeValue" } & Prisma.CallTypeValueFindManyArgs)
| ({ name: "addressValue" } & Prisma.AddressValueFindManyArgs);

export const permissionsForRouteType: Record<ValueType, Permissions[]> = {
ADDRESS: [Permissions.ManageValueAddress],
BLOOD_GROUP: [Permissions.ManageValueBloodGroup],
BUSINESS_ROLE: [Permissions.ManageValueBusinessRole],
CITIZEN_FLAG: [Permissions.ManageValueCitizenFlag],
Expand Down
1 change: 1 addition & 0 deletions apps/api/src/middlewares/ValidPath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const validPaths = [
"penal_code_group",
"qualification",
"call_type",
"address",
];

@Middleware()
Expand Down
4 changes: 3 additions & 1 deletion apps/client/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@
"user": "User",
"savedSettingsSuccess": "Settings successfully saved.",
"select": "Select...",
"item": "Item"
"item": "Item",
"address": "Address",
"location": "Location"
},
"Nav": {
"citizen": "Citizen",
Expand Down
7 changes: 7 additions & 0 deletions apps/client/locales/en/values.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"isConfidential": "Is Confidential",
"priority": "Priority",
"isDisabled": "Disabled",
"county": "County",
"alert_deletePenalCodeGroup": "Are you sure you want to delete <span>{group}</span>? This action cannot be undone.",
"alert_deleteValue": "Are you sure you want to delete <span>{value}</span>? This action cannot be undone!",
"alert_deleteSelectedValues": "Are you sure you want to delete {length} items? This action cannot be undone"
Expand Down Expand Up @@ -136,5 +137,11 @@
"ADD": "Add Call Type",
"DELETE": "Delete Call Type",
"EDIT": "Edit Call Type"
},
"ADDRESS": {
"MANAGE": "Manage Addresses",
"ADD": "Add Address",
"DELETE": "Delete Address",
"EDIT": "Edit Address"
}
}
2 changes: 1 addition & 1 deletion apps/client/src/components/admin/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ function SidebarItem({ route, href, text, isActive, onRouteClick }: ItemProps) {

return (
<li className="px-2">
<Link
<Link
onClick={onRouteClick}
className={classNames(
"transition-colors rounded-md block px-4 py-1 dark:text-white hover:bg-gray-200 dark:hover:bg-secondary",
Expand Down
4 changes: 4 additions & 0 deletions apps/client/src/components/admin/Sidebar/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ export const managementRoutes: SidebarRoute[] = [
];

export const valueRoutes: SidebarRoute[] = [
{
type: ValueType.ADDRESS,
permissions: [Permissions.ManageValueAddress],
},
{
type: ValueType.BLOOD_GROUP,
permissions: [Permissions.ManageValueBloodGroup],
Expand Down
7 changes: 7 additions & 0 deletions apps/client/src/components/admin/values/ManageValueModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {
isDLCategoryValue,
isCallTypeValue,
isOfficerRankValue,
isAddressValue,
} from "@snailycad/utils/typeguards";
import { QualificationFields } from "./manage-modal/QualificationFields";
import { ImageSelectInput, validateFile } from "components/form/inputs/ImageSelectInput";
Expand All @@ -53,6 +54,7 @@ import {
makeDefaultWhatPages,
} from "lib/admin/values/utils";
import { DivisionFields } from "./manage-modal/DivisionFields";
import { AddressFields } from "./manage-modal/AddressFields";

interface Props {
type: ValueType;
Expand Down Expand Up @@ -207,6 +209,9 @@ export function ManageValueModal({ onCreate, onUpdate, clType: dlType, type, val
officerRankDepartments:
value && isOfficerRankValue(value) ? defaultDepartments(value) : undefined,

postal: value && isAddressValue(value) ? value.postal ?? "" : "",
county: value && isAddressValue(value) ? value.county ?? "" : "",

showPicker: false,
image: "",
};
Expand Down Expand Up @@ -259,6 +264,8 @@ export function ManageValueModal({ onCreate, onUpdate, clType: dlType, type, val
<QualificationFields image={image} setImage={setImage} />
) : null}

{type === ValueType.ADDRESS ? <AddressFields /> : null}

{type === ValueType.BUSINESS_ROLE ? (
<SelectField
errorMessage={errors.as}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { TextField } from "@snailycad/ui";
import { useFormikContext } from "formik";

export function AddressFields() {
const { values, errors, setFieldValue } = useFormikContext<any>();

return (
<>
<TextField
label="Postal"
isOptional
name="postal"
onChange={(value) => setFieldValue("postal", value)}
value={values.postal}
errorMessage={errors.postal as string}
/>

<TextField
label="County"
isOptional
name="county"
onChange={(value) => setFieldValue("county", value)}
value={values.county}
errorMessage={errors.county as string}
/>
</>
);
}
23 changes: 2 additions & 21 deletions apps/client/src/components/business/CreateBusinessModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ import { CREATE_COMPANY_SCHEMA } from "@snailycad/schemas";
import { handleValidate } from "lib/handleValidate";
import { Toggle } from "components/form/Toggle";
import { useRouter } from "next/router";
import { FormRow } from "components/form/FormRow";
import { toastMessage } from "lib/toastMessage";
import { WhitelistStatus } from "@snailycad/types";
import { CitizenSuggestionsField } from "components/shared/CitizenSuggestionsField";
import type { GetBusinessesData, PostCreateBusinessData } from "@snailycad/types/api";
import { AddressPostalSelect } from "components/form/select/PostalSelect";

interface Props {
onCreate?(employee: GetBusinessesData["businesses"][number]): void;
Expand Down Expand Up @@ -87,26 +87,7 @@ export function CreateBusinessModal({ onCreate }: Props) {
value={values.name}
/>

<FormRow flexLike>
<TextField
errorMessage={errors.address}
label={t("address")}
name="address"
onChange={(value) => setFieldValue("address", value)}
value={values.address}
className="w-full"
/>

<TextField
isOptional
errorMessage={errors.postal}
label={common("postal")}
name="postal"
onChange={(value) => setFieldValue("postal", value)}
value={values.postal}
className="min-w-[200px]"
/>
</FormRow>
<AddressPostalSelect />

<FormField errorMessage={errors.whitelisted} label={t("whitelisted")}>
<Toggle
Expand Down
18 changes: 2 additions & 16 deletions apps/client/src/components/business/manage/BusinessTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import { handleValidate } from "lib/handleValidate";
import { Toggle } from "components/form/Toggle";
import { useRouter } from "next/router";
import { SettingsFormField } from "components/form/SettingsFormField";
import { FormRow } from "components/form/FormRow";
import type { DeleteBusinessByIdData, PutBusinessByIdData } from "@snailycad/types/api";
import { AddressPostalSelect } from "components/form/select/PostalSelect";

export function ManageBusinessTab() {
const { state, execute } = useFetch();
Expand Down Expand Up @@ -86,21 +86,7 @@ export function ManageBusinessTab() {
errorMessage={errors.address}
label={t("address")}
>
<FormRow flexLike>
<Input
className="w-full"
name="address"
onChange={handleChange}
value={values.address}
/>
<Input
className="w-[200px]"
name="postal"
onChange={handleChange}
value={values.postal}
placeholder={common("postal")}
/>
</FormRow>
<AddressPostalSelect />
</SettingsFormField>

<SettingsFormField
Expand Down
2 changes: 1 addition & 1 deletion apps/client/src/components/calls/TowTaxiCallsTable.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as React from "react";
import type * as React from "react";
import type { TaxiCall, TowCall } from "@snailycad/types";
import { Button } from "@snailycad/ui";
import { FullDate } from "components/shared/FullDate";
Expand Down
22 changes: 2 additions & 20 deletions apps/client/src/components/citizen/ManageCitizenForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
ManageLicensesFormFields,
} from "./licenses/ManageLicensesFormFields";
import parseISO from "date-fns/parseISO";
import { AddressPostalSelect } from "components/form/select/PostalSelect";

interface Props {
citizen: (Citizen & { user?: User | null }) | null;
Expand Down Expand Up @@ -233,26 +234,7 @@ export function ManageCitizenForm({
/>
</FormRow>

<FormRow flexLike>
<TextField
className="w-full"
errorMessage={errors.address}
label={t("address")}
value={values.address}
onChange={(value) => setFieldValue("address", value)}
name="address"
/>

<TextField
isOptional
className="w-full max-w-[200px]"
errorMessage={errors.postal}
label={common("postal")}
value={values.postal}
onChange={(value) => setFieldValue("postal", value)}
name="postal"
/>
</FormRow>
<AddressPostalSelect />

<TextField
isOptional
Expand Down
Loading

0 comments on commit 1c16a6f

Please sign in to comment.