diff --git a/editor.planx.uk/src/@planx/components/AddressInput/Public.tsx b/editor.planx.uk/src/@planx/components/AddressInput/Public.tsx index 3ae055715f..8a4081bc1d 100644 --- a/editor.planx.uk/src/@planx/components/AddressInput/Public.tsx +++ b/editor.planx.uk/src/@planx/components/AddressInput/Public.tsx @@ -1,7 +1,7 @@ import Card from "@planx/components/shared/Preview/Card"; import { CardHeader } from "@planx/components/shared/Preview/CardHeader/CardHeader"; import type { PublicProps } from "@planx/components/shared/types"; -import { useFormik } from "formik"; +import { FormikErrors, useFormik } from "formik"; import React from "react"; import InputLabel from "ui/public/InputLabel"; import Input from "ui/shared/Input/Input"; @@ -14,7 +14,7 @@ import { userDataSchema } from "./model"; export type Props = PublicProps; -interface FormProps { +export interface FormProps { line1: string; line2: string; town: string; @@ -50,16 +50,36 @@ export default function AddressInputComponent(props: Props): FCReturn { policyRef={props.policyRef} howMeasured={props.howMeasured} /> + + + ); +} + +interface AddressFieldsProps { + id?: string; + values: FormProps; + errors: FormikErrors; + handleChange: (e: React.ChangeEvent) => void; +} + +export function AddressFields(props: AddressFieldsProps): FCReturn { + return ( + <> - + ); } diff --git a/editor.planx.uk/src/@planx/components/AddressInput/model.ts b/editor.planx.uk/src/@planx/components/AddressInput/model.ts index c04c6350bb..e52164b2b2 100644 --- a/editor.planx.uk/src/@planx/components/AddressInput/model.ts +++ b/editor.planx.uk/src/@planx/components/AddressInput/model.ts @@ -12,14 +12,15 @@ export type Address = { country?: string; }; -export const userDataSchema: SchemaOf
= object({ - line1: string().required("Enter the first line of an address"), - line2: string(), - town: string().required("Enter a town"), - county: string(), - postcode: string().required("Enter a postcode"), - country: string(), -}); +export const userDataSchema = (_input: AddressInput): SchemaOf
=> + object({ + line1: string().required("Enter the first line of an address"), + line2: string(), + town: string().required("Enter a town"), + county: string(), + postcode: string().required("Enter a postcode"), + country: string(), + }); export interface AddressInput extends BaseNodeData { title: string; diff --git a/editor.planx.uk/src/@planx/components/List/Editor.tsx b/editor.planx.uk/src/@planx/components/List/Editor.tsx index 72b01dc3b7..32bde4b532 100644 --- a/editor.planx.uk/src/@planx/components/List/Editor.tsx +++ b/editor.planx.uk/src/@planx/components/List/Editor.tsx @@ -25,6 +25,7 @@ import { OpenSpaceGLA } from "./schemas/GLA/OpenSpace"; import { ParkingGLA } from "./schemas/GLA/ParkingGLA"; import { ProtectedSpaceGLA } from "./schemas/GLA/ProtectedSpace"; import { MaterialDetails } from "./schemas/Materials"; +import { Owners } from "./schemas/Owners"; import { Parking } from "./schemas/Parking"; import { ResidentialUnitsExisting } from "./schemas/ResidentialUnits/Existing"; import { ResidentialUnitsExistingLDCE } from "./schemas/ResidentialUnits/ExistingLDCE"; @@ -71,6 +72,7 @@ export const SCHEMAS = [ { name: "Parking details (GLA)", schema: ParkingGLA }, { name: "Trees", schema: Trees }, { name: "Trees (Map first)", schema: TreesMapFirst }, + { name: "Owners", schema: Owners }, ]; function ListComponent(props: Props) { diff --git a/editor.planx.uk/src/@planx/components/List/schemas/Owners.ts b/editor.planx.uk/src/@planx/components/List/schemas/Owners.ts new file mode 100644 index 0000000000..797132ec33 --- /dev/null +++ b/editor.planx.uk/src/@planx/components/List/schemas/Owners.ts @@ -0,0 +1,68 @@ +import { Schema } from "@planx/components/shared/Schema/model"; +import { TextInputType } from "@planx/components/TextInput/model"; + +export const Owners: Schema = { + type: "Person", + fields: [ + { + type: "question", + data: { + title: "What is the nature of their interest in the land?", + fn: "interest", + options: [ + { + id: "owner", + data: { text: "Owner", val: "owner" }, + }, + { + id: "lessee", + data: { text: "Lessee", val: "lessee" }, + }, + { + id: "occupier", + data: { text: "Occupier", val: "occupier" }, + }, + { + id: "other", + data: { text: "Something else", val: "other" }, + }, + ], + }, + }, + { + type: "text", + data: { + title: "What is their full name?", + fn: "name", + type: TextInputType.Short, + }, + }, + { + type: "address", + data: { + title: "What is their address?", + fn: "address", + }, + }, + { + type: "question", + data: { + title: "Have you notified them?", + description: + "Anyone with an interest in the land should be notified before submitting this application.", + fn: "noticeGiven", + options: [ + { + id: "yes", + data: { text: "Yes", val: "true" }, + }, + { + id: "no", + data: { text: "No", val: "false" }, + }, + ], + }, + }, + ], + min: 1, +} as const; diff --git a/editor.planx.uk/src/@planx/components/List/utils.tsx b/editor.planx.uk/src/@planx/components/List/utils.tsx index fd7f4c8d8d..af69b1c3cc 100644 --- a/editor.planx.uk/src/@planx/components/List/utils.tsx +++ b/editor.planx.uk/src/@planx/components/List/utils.tsx @@ -16,7 +16,7 @@ const List = styled("ul")(() => ({ * @returns string | React.JSX.Element - the `text` for the given value `val`, or the original value */ export function formatSchemaDisplayValue( - value: string | string[], + value: string | string[] | Record, field: Field, ) { switch (field.type) { @@ -24,6 +24,7 @@ export function formatSchemaDisplayValue( return field.data.units ? `${value} ${field.data.units}` : value; case "text": case "date": + case "address": return value; case "checklist": { const matchingOptions = field.data.options.filter((option) => diff --git a/editor.planx.uk/src/@planx/components/shared/Schema/InputFields/AddressFieldInput.tsx b/editor.planx.uk/src/@planx/components/shared/Schema/InputFields/AddressFieldInput.tsx new file mode 100644 index 0000000000..d1a9db61a2 --- /dev/null +++ b/editor.planx.uk/src/@planx/components/shared/Schema/InputFields/AddressFieldInput.tsx @@ -0,0 +1,36 @@ +import Box from "@mui/material/Box"; +import Typography from "@mui/material/Typography"; +import { + AddressFields, + FormProps, +} from "@planx/components/AddressInput/Public"; +import React from "react"; +import InputLegend from "ui/editor/InputLegend"; + +import { AddressField } from "../model"; +import { getFieldProps, Props } from "."; +import { FieldInputDescription } from "./shared"; + +export const AddressFieldInput: React.FC> = (props) => { + const { data, formik } = props; + const { id, errorMessage, value } = getFieldProps(props); + + return ( + + + + {data.title} + + + {data.description && ( + + )} + + + ); +}; diff --git a/editor.planx.uk/src/@planx/components/shared/Schema/InputFields/index.tsx b/editor.planx.uk/src/@planx/components/shared/Schema/InputFields/index.tsx index 5f73aed24b..7d7dcd91d8 100644 --- a/editor.planx.uk/src/@planx/components/shared/Schema/InputFields/index.tsx +++ b/editor.planx.uk/src/@planx/components/shared/Schema/InputFields/index.tsx @@ -7,6 +7,7 @@ import { get } from "lodash"; import React from "react"; import { exhaustiveCheck } from "utils"; +import { AddressFieldInput } from "./AddressFieldInput"; import { ChecklistFieldInput } from "./ChecklistFieldInput"; import { DateFieldInput } from "./DateFieldInput"; import { MapFieldInput } from "./MapFieldInput"; @@ -57,6 +58,8 @@ export const InputFields: React.FC> = (props) => { return ; case "map": return ; + case "address": + return ; default: return exhaustiveCheck(type); } diff --git a/editor.planx.uk/src/@planx/components/shared/Schema/model.ts b/editor.planx.uk/src/@planx/components/shared/Schema/model.ts index 7e4998a17b..26c2e8aa68 100644 --- a/editor.planx.uk/src/@planx/components/shared/Schema/model.ts +++ b/editor.planx.uk/src/@planx/components/shared/Schema/model.ts @@ -1,3 +1,7 @@ +import { + AddressInput, + userDataSchema as addressValidationSchema, +} from "@planx/components/AddressInput/model"; import { Feature } from "geojson"; import { exhaustiveCheck } from "utils"; import { array, BaseSchema, object, ObjectSchema, string } from "yup"; @@ -81,6 +85,11 @@ export type DateField = { data: DateInput & { fn: string }; }; +export type AddressField = { + type: "address"; + data: AddressInput & { fn: string }; +}; + export type MapField = { type: "map"; data: { @@ -106,6 +115,7 @@ export type Field = | QuestionField | ChecklistField | DateField + | AddressField | MapField; /** @@ -120,8 +130,8 @@ export interface Schema { export type SchemaUserResponse = Record< Field["data"]["fn"], - string | string[] | any[] ->; // string | string[] | Feature[] + string | string[] | Record | any[] +>; // string | string[] | Record | Feature[] /** * Output data from a form using the useSchema hook @@ -156,6 +166,9 @@ const generateValidationSchemaForFields = ( case "date": fieldSchemas[data.fn] = dateValidationSchema(data); break; + case "address": + fieldSchemas[data.fn] = addressValidationSchema(data); + break; case "map": fieldSchemas[data.fn] = mapValidationSchema(data); break; @@ -187,9 +200,13 @@ export const generateValidationSchema = (schema: Schema) => { export const generateInitialValues = (schema: Schema): SchemaUserResponse => { const initialValues: SchemaUserResponse = {}; schema.fields.forEach((field) => { - ["checklist", "map"].includes(field.type) - ? (initialValues[field.data.fn] = []) - : (initialValues[field.data.fn] = ""); + if (["checklist", "map"].includes(field.type)) { + initialValues[field.data.fn] = []; + } else if (field.type === "address") { + initialValues[field.data.fn] = {}; + } else { + initialValues[field.data.fn] = ""; + } }); return initialValues; };