From b1fe57fc396b8de840ba8c34d9c12f8e8075b149 Mon Sep 17 00:00:00 2001 From: thatmattlove Date: Wed, 14 Aug 2024 13:37:56 -0400 Subject: [PATCH] add deal reg form handler; fix various form issues --- lib/address-types.ts | 21 +++++++++++--------- lib/api/form/deal-registration.ts | 21 ++++++++++++++++++++ lib/api/form/index.ts | 1 + lib/server/address.ts | 5 +++-- pages/api/form/[name].ts | 2 ++ src/components/form/checkbox-field.tsx | 2 +- src/components/form/currency-field.tsx | 2 +- src/components/form/date-field.tsx | 10 ++++------ src/components/form/schema.ts | 27 +++++++++++++++++++++----- src/components/form/select-field.tsx | 13 +++++++++---- src/hooks/use-address-search.ts | 6 +++++- 11 files changed, 81 insertions(+), 29 deletions(-) create mode 100644 lib/api/form/deal-registration.ts diff --git a/lib/address-types.ts b/lib/address-types.ts index 19b2521..cb15b13 100644 --- a/lib/address-types.ts +++ b/lib/address-types.ts @@ -1,6 +1,7 @@ export interface SearchResult { - address: string; - name: string; + displayName: string; + description: string; + formValue: string; } export interface SearchError { @@ -18,10 +19,12 @@ export function isSearchResults(data: unknown): data is SearchResult[] { return ( typeof data[0] === "object" && data[0] !== null && - "address" in data[0] && - "name" in data[0] && - typeof data[0].address === "string" && - typeof data[0].name === "string" + "displayName" in data[0] && + "formValue" in data[0] && + "description" in data[0] && + typeof data[0].displayName === "string" && + typeof data[0].formValue === "string" && + typeof data[0].description === "string" ); } return Array.isArray(data); @@ -43,14 +46,14 @@ export function isMapboxCity(data: Suggestion): boolean { } export function formatCity(data: Suggestion): SearchResult { - const name = [data.name, data.context.region.region_code].join(", "); + const displayName = [data.name, data.context.region.region_code].join(", "); let addressParts: string[] = []; if (typeof data.context.district !== "undefined") { addressParts = [...addressParts, data.context.district.name]; } addressParts = [...addressParts, data.context.region.name, data.context.country.name]; - const address = addressParts.join(", "); - return { name, address }; + const description = addressParts.join(", "); + return { displayName, description, formValue: displayName }; } export interface MapboxSearchResult { diff --git a/lib/api/form/deal-registration.ts b/lib/api/form/deal-registration.ts new file mode 100644 index 0000000..8a1c6e9 --- /dev/null +++ b/lib/api/form/deal-registration.ts @@ -0,0 +1,21 @@ +import { isbot } from "isbot"; +import type { NextApiRequest } from "next"; +import { isValidJsonRequest } from "~/lib"; + +export async function handleDealRegistrationForm(request: NextApiRequest): Promise { + if (!isValidJsonRequest(request)) { + throw new Error("Invalid request payload"); + } + if (isbot(request.headers["user-agent"])) { + throw new Error("You seem like a robot"); + } + let form = "unknown"; + if (typeof request.url !== "undefined") { + const [withoutQuery] = request.url.split("?"); + const paths = withoutQuery.split("/"); + form = paths[paths.length - 1]; + } + const data = request.body; + console.log(form, data); + return new Response("neat", { status: 200 }); +} diff --git a/lib/api/form/index.ts b/lib/api/form/index.ts index 884b9c0..2325b38 100644 --- a/lib/api/form/index.ts +++ b/lib/api/form/index.ts @@ -1,3 +1,4 @@ +export * from "./deal-registration"; export * from "./partnership"; export * from "./sales"; export * from "./subscribe"; diff --git a/lib/server/address.ts b/lib/server/address.ts index 2fc4fcf..ee66da2 100644 --- a/lib/server/address.ts +++ b/lib/server/address.ts @@ -40,8 +40,9 @@ export async function search( return results; } const results: SearchResult[] = data.suggestions.map(suggestion => ({ - address: suggestion.full_address, - name: suggestion.name, + displayName: suggestion.name, + formValue: suggestion.full_address, + description: suggestion.full_address, })); return results; } diff --git a/pages/api/form/[name].ts b/pages/api/form/[name].ts index ac26d91..fdf0284 100644 --- a/pages/api/form/[name].ts +++ b/pages/api/form/[name].ts @@ -1,4 +1,5 @@ import { + handleDealRegistrationForm, handlePartnershipForm, handleSalesForm, handleSubscribe, @@ -18,6 +19,7 @@ const FORM_MAP = new Map([ ["security-demo", handleSalesForm], ["trial", handleTrialForm], ["partnership-inquiry", handlePartnershipForm], + ["deal-registration", handleDealRegistrationForm], ]); function isValidForm(form: unknown): form is FormKeys { diff --git a/src/components/form/checkbox-field.tsx b/src/components/form/checkbox-field.tsx index a9061c4..018eb60 100644 --- a/src/components/form/checkbox-field.tsx +++ b/src/components/form/checkbox-field.tsx @@ -56,7 +56,7 @@ export const CheckboxField = ( ) : ( - + {opts.map(opt => ( ( {field.unitSymbol} - + (props: DateFieldProps({}); - const [formValue, setFormValue] = useState(""); const [displayValue, setDisplayValue] = useState(); const initialRef = useRef(null); - const { control } = useFormContext(); + const { control, register } = useFormContext(); const { - field: { onChange, ref }, + field: { onChange }, fieldState: { error }, } = useController({ name, control, rules: { required }, defaultValue }); const onDateChange = (changed: CalendarDate | CalendarValues): void => { if (changed instanceof Date) { setDisplayValue(changed.toLocaleDateString(undefined, { dateStyle: "full" })); - setFormValue(changed.toISOString()); - onChange(formValue); + onChange(changed); onClose(); setDates({ start: changed, end: changed }); } @@ -79,7 +77,7 @@ export const DateField = (props: DateFieldProps {label ? label : displayName} - + {typeof error !== "undefined" && error.message} diff --git a/src/components/form/schema.ts b/src/components/form/schema.ts index 20f6cea..c7a5524 100644 --- a/src/components/form/schema.ts +++ b/src/components/form/schema.ts @@ -15,6 +15,7 @@ import { TextInputValidationType } from "~/types"; import { isAddressSearchField, isCheckboxField, + isCurrencyField, isDateField, isSelectField, isTextAreaField, @@ -62,12 +63,19 @@ export function createSchema( // Checkbox or Select if (isCheckboxField(fieldConfig) || isSelectField(fieldConfig)) { - value = z.array(z.string()); + if (fieldConfig.multiple) { + value = z.array(z.string()); - if (fieldConfig.required) { - value = createRequiredArray(fieldConfig); - } + if (fieldConfig.required) { + value = createRequiredArray(fieldConfig); + } + } else { + value = z.string(); + if (fieldConfig.required) { + value = createRequiredString(fieldConfig); + } + } // Add field to schema is(value) && (final[fieldConfig.formId] = value); return final; @@ -80,7 +88,16 @@ export function createSchema( value = z.date(); is(value) && (final[fieldConfig.formId] = value); return final; - } // Text Area + } // Currency Field + else if (isCurrencyField(fieldConfig)) { + value = z.coerce.number(); + if (fieldConfig.required) { + value = value.min(1); + } + is(value) && (final[fieldConfig.formId] = value); + return final; + } + // Text Area else if (isTextAreaField(fieldConfig)) { value = z.string(); diff --git a/src/components/form/select-field.tsx b/src/components/form/select-field.tsx index 4ddceb9..dceb392 100644 --- a/src/components/form/select-field.tsx +++ b/src/components/form/select-field.tsx @@ -71,14 +71,19 @@ export const SelectField = (props: SelectFieldProps) => { const handleSelect = useCallback( (values: readonly SelectOptionSingle[]) => { - if (values.length === 0) { + if (values.length === 0 || values === null) { setValue(name, defaultValue); onChange(defaultValue); return; } - const labels = values.filter(v => !!v.label).map(v => v.label); - setValue(name, labels); - onChange(labels); + if (isMulti) { + const labels = values.filter(v => !!v.label).map(v => v.label); + setValue(name, labels); + onChange(labels); + } else { + setValue(name, values[0].label); + onChange(values[0].label); + } }, [name, setValue], ); diff --git a/src/hooks/use-address-search.ts b/src/hooks/use-address-search.ts index 1f53856..cf0e377 100644 --- a/src/hooks/use-address-search.ts +++ b/src/hooks/use-address-search.ts @@ -16,7 +16,11 @@ async function queryFn(search: string, locationType: LocationType): Promise ({ label: d.name, value: d.address, description: d.address })); + return data.map(d => ({ + label: d.displayName, + value: d.formValue, + description: d.description, + })); } throw new Error(data); }