diff --git a/doc/how-to/how-to-add-a-list-component-schema.md b/doc/how-to/how-to-add-a-list-component-schema.md index fc1b712f72..fe40164956 100644 --- a/doc/how-to/how-to-add-a-list-component-schema.md +++ b/doc/how-to/how-to-add-a-list-component-schema.md @@ -12,7 +12,7 @@ The ideal maintainers of these schemas are still the services team though, rathe 2. **GitHub** - In the `.ts` file, ensure the schema has this basic structure: ```ts -import { Schema } from "@planx/components/List/model"; +import { Schema } from "@planx/component/shared/Schema/model"; export const YourSchemasName: Schema = { type: "Title (singular if no max, plural if max = 1)", diff --git a/doc/how-to/how-to-setup-metabase-for-a-new-team-or-service.md b/doc/how-to/how-to-setup-metabase-for-a-new-team-or-service.md new file mode 100644 index 0000000000..4ecad896d3 --- /dev/null +++ b/doc/how-to/how-to-setup-metabase-for-a-new-team-or-service.md @@ -0,0 +1,61 @@ +# How to setup a Metabase collection for a new team + +## What is Metabase? +[Metabase](https://www.metabase.com/) is an open source BI service which we self-host as part of PlanX. It allows teams to view and self-serve analytics dashboards related to their flows, applications, and users. + +Metabase is set up and running on both Staging and Production environments, but only the Production instance (with Production data) has dashboards maintained and curated for teams. + +## Context +The Metabase analytics dashboards are currently created per-published service. + +We would like to automate this process as much as possible, but currently it is _all manual_. + +This means that each time a council goes live with a service, they let us know (eg via Slack) and we go through the process below. + +## Process + +To do any of the below, you will need to be logged into the Production Metabase service at https://metabase.editor.planx.uk/. Login details are stored on the OSL 1Password account. + +### Setting up Metabase for a new team + +_This happens only if a team is new / about to publish their first live service._ + +1. Check if a team Collection (folder) already exists in Metabase. If not, create a new one. + +![Screenshot - Add a Collection](./images/setup-metabase/new_collection.png) + +### Setting up Metabase for a new Service + +_This happens after a team lets us know that a service is going live._ + +1. Enter the 'Templates' Collection using the navigation bar on the left and duplicate the relevant template, replacing 'Template' with the council name. Duplicate it into the relevant team's Collection (folder). + +![Screenshot - Templates Collection](./images/setup-metabase/templates.png) +![Screenshot - Duplicate the dashboard](./images/setup-metabase/duplicate_a_dashboard.png) + + * Ensure "Only duplicate the dashboard" is selected. This avoids unecessarily duplicating the visualisations which we maintain, and also means if we need to update anything, the changes will propagate across the dashboards. + +![Screenshot - Only duplicate the dashboard](./images/setup-metabase/only_duplicate_dashboard.png) + + * Not all teams host the same services on PlanX. Ensure you only duplicate Dashboards for public, live flows. This can be checked via the PlanX Editor. We currently only have templates for three services: Find Out If You Need Planning Permission, Apply for a Lawful Development Service, and Report a Breach. + +2. Navigate to the new Dashboard to update the team slug and service slug default value variables. + +![Screenshot - Team slug variable](./images/setup-metabase/team_slug_default_value.png) +![Screenshot - Service slug variable](./images/setup-metabase/service_slug_default_value.png) + + * Edit Dashboard > \['Team slug' or 'Service slug'\] > Default Value > Enter > Done > Save* + + * This variable ensures that the dashboard is looking at analytics and statistics for the correct flow. + +3. You should now see the chart update, and the variables in the top left match the slugs for the new team's flow. +![Screenshot - Default values are being filtered for](./images/setup-metabase/default_values_filtered.png) + +4. Enable sharing by turning the 'Public' link on. + +![Screenshot - Enable sharing](./images/setup-metabase/enable_sharing.png) +![Screenshot - Share Dashboard link with team by turning 'Public' link on.](./images/setup-metabase/share_with_team.png) + +5. Add the dashboard, private and public links to the OSL internal 'Analytics overview' Notion page (PlanX > Analytics overview). + +6. To add the link to the editor, update the `flows.analytics_link` column in Hasura with the public URL (generated in step 4), for each flow a dashboard has been generated for. diff --git a/doc/how-to/how-to-setup-metabase-for-a-new-team.md b/doc/how-to/how-to-setup-metabase-for-a-new-team.md deleted file mode 100644 index 03727a8d7a..0000000000 --- a/doc/how-to/how-to-setup-metabase-for-a-new-team.md +++ /dev/null @@ -1,61 +0,0 @@ -# How to setup a Metabase collection for a new team - -## What is Metabase? -[Metabase](https://www.metabase.com/) is an open source BI service which we self-host as part of PlanX. It allows teams to view and self-serve analytics dashboards related to their flows, applications, and users. - -Metabase is set up and running on both Staging and Production environments, but only the Production instance (with Production data) has dashboards maintained and curated for teams. - - -## Process -1. Log into the Production Metabase service at https://metabase.editor.planx.uk/. Login details are stored on the OSL 1Password account. - -2. Create new Collection for the team - -![Screenshot - Add a Collection](./images/setup-metabase/new_collection.png) - -3. Duplicate existing Dashboards (FOIYNPP, LDC) from an existing team, renaming them and adding to new Collection. - -![Screenshot - Duplicate and existing dashboard](./images/setup-metabase/duplicate_an_existing_dashboard.png) - - * Not all teams host the same services on PlanX. Ensure you only duplicate Dashboards for which teams have an associated flows. This can be checked via the PlanX Editor. - -![Screenshot - Only duplicate the dashboard](./images/setup-metabase/only_duplicate_dashboard.png) - - * Ensure "Only duplicate the dashboard" is selected. This avoids unecessarily duplicating the visualisations which we maintain. - -4. Navigate to the new Collection, and edit each Dashboard to update the FlowID variable. - -![Screenshot - FlowID variable](./images/setup-metabase/flow_id.png) -![Screenshot - Updating the default value for FlowID](./images/setup-metabase/default_value.png) -*Edit Dashboard > FlowID > Default Value > Enter > Done > Save* - -This variable ensures that the dashboard is looking at analytics and statistics for the correct flow. - -Flow IDs can be obtained via Hasura with the following query, substituting in the `TEAM_SLUG` variable - - -```graphql -query GetFlowIDsForTeam { - published_flows(where: {flow: {team: {slug: {_eq: }}}}, distinct_on: flow_id) { - flow_id - flow { - slug - } - } -} -``` - -5. You should now see the chart update, and the FlowID variable in the top left match the ID for the new team's flow. - -6. Enable sharing - -![Screenshot - Enable sharing](./images/setup-metabase/enable_sharing.png) - -7. Share the public dashboard link with the relevant team directly - -![Screenshot - Share Dashboard link with team](./images/setup-metabase/share_with_team.png) - -8. Update "ODP Useful Links" document on Notion -* Link: [https://www.notion.so/opensystemslab/Open-Digital-Planning-Useful-links-176e31a4d1274c82959d83fe503d274f](https://www.notion.so/opensystemslab/Plan-Service-Content-d579ddc3f4f3472b8fe167865ede2e93?pvs=4#e637abed931d4697a2707455d81e4620)https://www.notion.so/opensystemslab/Plan-Service-Content-d579ddc3f4f3472b8fe167865ede2e93?pvs=4#e637abed931d4697a2707455d81e4620 -* Dashboards are listed under PlanX Service Content > BETA services & analytics > {Council}) - -9. Update the `flows.analytics_link` column in Hasura with the public URL (generated in step 6), for each flow a dashboard has been generated for diff --git a/doc/how-to/images/setup-metabase/default_value.png b/doc/how-to/images/setup-metabase/default_value.png deleted file mode 100644 index 7ac8570725..0000000000 Binary files a/doc/how-to/images/setup-metabase/default_value.png and /dev/null differ diff --git a/doc/how-to/images/setup-metabase/default_values_filtered.png b/doc/how-to/images/setup-metabase/default_values_filtered.png new file mode 100644 index 0000000000..330179c768 Binary files /dev/null and b/doc/how-to/images/setup-metabase/default_values_filtered.png differ diff --git a/doc/how-to/images/setup-metabase/duplicate_a_dashboard.png b/doc/how-to/images/setup-metabase/duplicate_a_dashboard.png new file mode 100644 index 0000000000..5136c3e956 Binary files /dev/null and b/doc/how-to/images/setup-metabase/duplicate_a_dashboard.png differ diff --git a/doc/how-to/images/setup-metabase/duplicate_an_existing_dashboard.png b/doc/how-to/images/setup-metabase/duplicate_an_existing_dashboard.png deleted file mode 100644 index 513cd35821..0000000000 Binary files a/doc/how-to/images/setup-metabase/duplicate_an_existing_dashboard.png and /dev/null differ diff --git a/doc/how-to/images/setup-metabase/flow_id.png b/doc/how-to/images/setup-metabase/flow_id.png deleted file mode 100644 index aa6a8f600e..0000000000 Binary files a/doc/how-to/images/setup-metabase/flow_id.png and /dev/null differ diff --git a/doc/how-to/images/setup-metabase/service_slug_default_value.png b/doc/how-to/images/setup-metabase/service_slug_default_value.png new file mode 100644 index 0000000000..8ddf39b363 Binary files /dev/null and b/doc/how-to/images/setup-metabase/service_slug_default_value.png differ diff --git a/doc/how-to/images/setup-metabase/team_slug_default_value.png b/doc/how-to/images/setup-metabase/team_slug_default_value.png new file mode 100644 index 0000000000..3de46bcac3 Binary files /dev/null and b/doc/how-to/images/setup-metabase/team_slug_default_value.png differ diff --git a/doc/how-to/images/setup-metabase/templates.png b/doc/how-to/images/setup-metabase/templates.png new file mode 100644 index 0000000000..b842762aa1 Binary files /dev/null and b/doc/how-to/images/setup-metabase/templates.png differ diff --git a/editor.planx.uk/src/@planx/components/Confirmation/Public.test.tsx b/editor.planx.uk/src/@planx/components/Confirmation/Public.test.tsx index 188518bdbd..5d98b00970 100644 --- a/editor.planx.uk/src/@planx/components/Confirmation/Public.test.tsx +++ b/editor.planx.uk/src/@planx/components/Confirmation/Public.test.tsx @@ -18,6 +18,7 @@ vi.mock("@opensystemslab/planx-core", () => { it("should not have any accessibility violations", async () => { const { container } = setup( { expect(result).toBe(false); }); + it("rejects slots which failed to upload", async () => { + const mockSlots = [ + { status: "error" }, + { status: "success" }, + ] as FileUploadSlot[]; + + const result = await slotsSchema.isValid(mockSlots, { + context: { fileList: mockFileList }, + }); + + expect(result).toBe(false); + }); + it("allows slots with all files uploaded", async () => { const mockSlots = [ { status: "success" }, @@ -197,6 +210,40 @@ describe("slotSchema", () => { expect(result).toBe(true); }); + + it("allows users to proceed if there are no required files", async () => { + const mockSlots: FileUploadSlot[] = []; + + const result = await slotsSchema.isValid(mockSlots, { + context: { + fileList: { + required: [], + recommended: [ + { ...mockFileTypes.AlwaysRecommended, slots: [{ id: "123" }] }, + ], + optional: [{ ...mockFileTypes.NotRequired, slots: [{ id: "456" }] }], + }, + }, + }); + + expect(result).toBe(true); + }); + + it("allows users to proceed if there are no required files, and they have not uploaded any optional or recommended files", async () => { + const mockSlots: FileUploadSlot[] = []; + + const result = await slotsSchema.isValid(mockSlots, { + context: { + fileList: { + required: [], + recommended: [{ ...mockFileTypes.AlwaysRecommended }], + optional: [{ ...mockFileTypes.NotRequired }], + }, + }, + }); + + expect(result).toBe(true); + }); }); describe("fileLabelSchema", () => { diff --git a/editor.planx.uk/src/@planx/components/FileUploadAndLabel/schema.ts b/editor.planx.uk/src/@planx/components/FileUploadAndLabel/schema.ts index 25d82a238b..1b6df3530b 100644 --- a/editor.planx.uk/src/@planx/components/FileUploadAndLabel/schema.ts +++ b/editor.planx.uk/src/@planx/components/FileUploadAndLabel/schema.ts @@ -79,12 +79,11 @@ export const slotsSchema = array() if (!slots) throw new Error("Missing slots for slotsSchema"); const { fileList } = context as SlotsSchemaTestContext; - const allFilesOptional = - !fileList.recommended.length && !fileList.required.length; - const isMinFileUploadCountSatisfied = - allFilesOptional || slots.length > 0; + const noFilesAreRequired = Boolean(!fileList.required.length); + if (noFilesAreRequired) return true; - return isMinFileUploadCountSatisfied; + const isAtLeastOneFileUploaded = slots.length > 0; + return isAtLeastOneFileUploaded; }, }) .test({ @@ -101,11 +100,8 @@ export const slotsSchema = array() name: "errorStatus", message: "Remove files which failed to upload", test: (slots?: Array) => { - return Boolean( - slots && - slots.length > 0 && - !slots.some((slot) => slot.status === "error"), - ); + const didAnyUploadFail = slots?.some((slot) => slot.status === "error"); + return !didAnyUploadFail; }, }); diff --git a/editor.planx.uk/src/@planx/components/List/Public/Context.tsx b/editor.planx.uk/src/@planx/components/List/Public/Context.tsx index 8d7f033828..bf50c5f481 100644 --- a/editor.planx.uk/src/@planx/components/List/Public/Context.tsx +++ b/editor.planx.uk/src/@planx/components/List/Public/Context.tsx @@ -1,3 +1,9 @@ +import { useSchema } from "@planx/components/shared/Schema/hook"; +import { + Schema, + SchemaUserData, + SchemaUserResponse, +} from "@planx/components/shared/Schema/model"; import { getPreviouslySubmittedData, makeData, @@ -11,14 +17,7 @@ import React, { useState, } from "react"; -import { - generateInitialValues, - generateValidationSchema, - List, - Schema, - UserData, - UserResponse, -} from "../model"; +import { List } from "../model"; import { flatten, sumIdenticalUnits, @@ -33,7 +32,7 @@ interface ListContextValue { removeItem: (index: number) => void; editItem: (index: number) => void; cancelEditItem: () => void; - formik: FormikProps; + formik: FormikProps; validateAndSubmitForm: () => void; listProps: PublicProps; /** @@ -58,13 +57,57 @@ const ListContext = createContext(undefined); export const ListProvider: React.FC = (props) => { const { schema, children, handleSubmit } = props; + const { formikConfig, initialValues } = useSchema({ + schema, + previousValues: getPreviouslySubmittedData(props), + }); + + const formik = useFormik({ + ...formikConfig, + onSubmit: (values) => { + // defaultPassportData (array) is used when coming "back" + const defaultPassportData = makeData(props, values.schemaData)?.["data"]; + + // flattenedPassportData makes individual list items compatible with Calculate components + const flattenedPassportData = flatten(defaultPassportData, { depth: 2 }); + + // basic example of general summary stats we can add onSubmit: + // 1. count of items/responses + // 2. if the schema includes a field that sets fn = "identicalUnits", sum of total units + // 3. if the schema includes a field that sets fn = "development" & fn = "identicalUnits", sum of total units by development "val" + const totalUnits = sumIdenticalUnits(props.fn, defaultPassportData); + const totalUnitsByDevelopmentType = sumIdenticalUnitsByDevelopmentType( + props.fn, + defaultPassportData, + ); + + const summaries = { + [`${props.fn}.total.listItems`]: + defaultPassportData[`${props.fn}`].length, + ...(totalUnits > 0 && { + [`${props.fn}.total.units`]: totalUnits, + }), + ...(totalUnits > 0 && + Object.keys(totalUnitsByDevelopmentType).length > 0 && + totalUnitsByDevelopmentType), + }; + + handleSubmit?.({ + data: { + ...defaultPassportData, + ...flattenedPassportData, + ...summaries, + }, + }); + }, + }); const [activeIndex, setActiveIndex] = useState( props.previouslySubmittedData ? -1 : 0, ); const [activeItemInitialState, setActiveItemInitialState] = useState< - UserResponse | undefined + SchemaUserResponse | undefined >(undefined); const [addItemError, setAddItemError] = useState(false); @@ -85,21 +128,21 @@ export const ListProvider: React.FC = (props) => { if (activeIndex !== -1) return setAddItemError(true); // Do not allow new item to be added if it will exceed max - if (schema.max && formik.values.userData.length === schema.max) { + if (schema.max && formik.values.schemaData.length === schema.max) { return setMaxError(true); } // Add new item, and set to active setAddItemError(false); - formik.values.userData.push(generateInitialValues(schema)); - setActiveIndex(formik.values.userData.length - 1); + formik.values.schemaData.push(initialValues); + setActiveIndex(formik.values.schemaData.length - 1); }; const saveItem = async () => { resetErrors(); const errors = await formik.validateForm(); - const isValid = !errors.userData?.length; + const isValid = !errors.schemaData?.length; if (isValid) { exitEditMode(); setAddItemError(false); @@ -114,10 +157,10 @@ export const ListProvider: React.FC = (props) => { setActiveIndex((prev) => (prev === -1 ? 0 : prev - 1)); } - // Remove item from userData + // Remove item from schemaData formik.setFieldValue( - "userData", - formik.values.userData.filter((_, i) => i !== index), + "schemaData", + formik.values.schemaData.filter((_, i) => i !== index), ); }; @@ -126,7 +169,7 @@ export const ListProvider: React.FC = (props) => { if (activeIndex !== -1) return setUnsavedItemError(true); // Manually validate minimum number of items - if (formik.values.userData.length < schema.min) { + if (formik.values.schemaData.length < schema.min) { return setMinError(true); } @@ -144,69 +187,17 @@ export const ListProvider: React.FC = (props) => { }; const editItem = (index: number) => { - setActiveItemInitialState(formik.values.userData[index]); + setActiveItemInitialState(formik.values.schemaData[index]); setActiveIndex(index); }; - const getInitialValues = () => { - const previousValues = getPreviouslySubmittedData(props); - if (previousValues) return previousValues; - - return schema.min ? [generateInitialValues(schema)] : []; - }; - const exitEditMode = () => setActiveIndex(-1); const resetItemToPreviousState = () => - formik.setFieldValue(`userData[${activeIndex}]`, activeItemInitialState); + formik.setFieldValue(`schemaData[${activeIndex}]`, activeItemInitialState); const isPageComponent = schema.max === 1; - const formik = useFormik({ - initialValues: { - userData: getInitialValues(), - }, - onSubmit: (values) => { - // defaultPassportData (array) is used when coming "back" - const defaultPassportData = makeData(props, values.userData)?.["data"]; - - // flattenedPassportData makes individual list items compatible with Calculate components - const flattenedPassportData = flatten(defaultPassportData, { depth: 2 }); - - // basic example of general summary stats we can add onSubmit: - // 1. count of items/responses - // 2. if the schema includes a field that sets fn = "identicalUnits", sum of total units - // 3. if the schema includes a field that sets fn = "development" & fn = "identicalUnits", sum of total units by development "val" - const totalUnits = sumIdenticalUnits(props.fn, defaultPassportData); - const totalUnitsByDevelopmentType = sumIdenticalUnitsByDevelopmentType( - props.fn, - defaultPassportData, - ); - - const summaries = { - [`${props.fn}.total.listItems`]: - defaultPassportData[`${props.fn}`].length, - ...(totalUnits > 0 && { - [`${props.fn}.total.units`]: totalUnits, - }), - ...(totalUnits > 0 && - Object.keys(totalUnitsByDevelopmentType).length > 0 && - totalUnitsByDevelopmentType), - }; - - handleSubmit?.({ - data: { - ...defaultPassportData, - ...flattenedPassportData, - ...summaries, - }, - }); - }, - validateOnBlur: false, - validateOnChange: false, - validationSchema: generateValidationSchema(schema), - }); - return ( = T & { id: string }; - -export const TextFieldInput: React.FC> = ({ id, data }) => { - const { formik, activeIndex } = useListContext(); - - return ( - - { - if (type === "email") return "email"; - else if (type === "phone") return "tel"; - return "text"; - })(data.type)} - multiline={data.type && ["long", "extraLong"].includes(data.type)} - bordered - value={formik.values.userData[activeIndex][data.fn]} - onChange={formik.handleChange} - errorMessage={get(formik.errors, ["userData", activeIndex, data.fn])} - id={id} - rows={ - data.type && ["long", "extraLong"].includes(data.type) ? 5 : undefined - } - name={`userData[${activeIndex}]['${data.fn}']`} - required - inputProps={{ - "aria-describedby": [ - data.description ? DESCRIPTION_TEXT : "", - get(formik.errors, ["userData", activeIndex, data.fn]) - ? `${ERROR_MESSAGE}-${id}` - : "", - ] - .filter(Boolean) - .join(" "), - }} - /> - - ); -}; - -export const NumberFieldInput: React.FC> = ({ - id, - data, -}) => { - const { formik, activeIndex } = useListContext(); - - return ( - - - - {data.units && {data.units}} - - - ); -}; - -export const RadioFieldInput: React.FC> = (props) => { - const { formik, activeIndex } = useListContext(); - const { id, data } = props; - - return ( - - ({ - color: theme.palette.text.primary, - "&.Mui-focused": { - color: theme.palette.text.primary, - }, - })} - > - {data.title} - - - - {data.options.map(({ id, data }) => ( - - ))} - - - - ); -}; - -export const SelectFieldInput: React.FC> = (props) => { - const { formik, activeIndex } = useListContext(); - const { id, data } = props; - - return ( - - - - {data.options.map((option) => ( - - {option.data.text} - - ))} - - - - ); -}; - -export const ChecklistFieldInput: React.FC> = (props) => { - const { formik, activeIndex } = useListContext(); - const { - id, - data: { options, title, fn }, - } = props; - - const changeCheckbox = - (id: string) => - async ( - _checked: React.MouseEvent | undefined, - ) => { - let newCheckedIds; - - if (formik.values.userData[activeIndex][fn].includes(id)) { - newCheckedIds = ( - formik.values.userData[activeIndex][fn] as string[] - ).filter((x) => x !== id); - } else { - newCheckedIds = [...formik.values.userData[activeIndex][fn], id]; - } - - await formik.setFieldValue( - `userData[${activeIndex}]['${fn}']`, - newCheckedIds, - ); - }; - - return ( - - - - {title} - {options.map((option) => ( - - ))} - - - - ); -}; - -export const DateFieldInput: React.FC> = ({ id, data }) => { - const { formik, activeIndex } = useListContext(); - - return ( - - - { - formik.setFieldValue( - `userData[${activeIndex}]['${data.fn}']`, - paddedDate(newDate, eventType), - ); - }} - error={get(formik.errors, ["userData", activeIndex, data.fn])} - id={id} - /> - - - ); -}; - -export const MapFieldInput: React.FC> = (props) => { - const { formik, activeIndex, schema } = useListContext(); - const { - id, - data: { title, fn, mapOptions }, - } = props; - - const teamSettings = useStore.getState().teamSettings; - const passport = useStore((state) => state.computePassport()); - - const [_features, setFeatures] = useState(undefined); - - useEffect(() => { - const geojsonChangeHandler = async ({ detail: geojson }: any) => { - if (geojson["EPSG:3857"]?.features) { - setFeatures(geojson["EPSG:3857"].features); - await formik.setFieldValue( - `userData[${activeIndex}]['${fn}']`, - geojson["EPSG:3857"].features, - ); - } else { - // if the user clicks 'reset' on the map, geojson will be empty object, so set features to undefined - setFeatures(undefined); - await formik.setFieldValue( - `userData[${activeIndex}]['${fn}']`, - undefined, - ); - } - }; - - const map: any = document.getElementById(id); - - map?.addEventListener("geojsonChange", geojsonChangeHandler); - - return function cleanup() { - map?.removeEventListener("geojsonChange", geojsonChangeHandler); - }; - }, [setFeatures]); - - return ( - - - - {/* @ts-ignore */} - - - - - ); -}; diff --git a/editor.planx.uk/src/@planx/components/List/Public/index.tsx b/editor.planx.uk/src/@planx/components/List/Public/index.tsx index 56cfe6984d..8783f7ed3c 100644 --- a/editor.planx.uk/src/@planx/components/List/Public/index.tsx +++ b/editor.planx.uk/src/@planx/components/List/Public/index.tsx @@ -8,26 +8,20 @@ import TableBody from "@mui/material/TableBody"; import TableCell from "@mui/material/TableCell"; import TableRow from "@mui/material/TableRow"; import Typography from "@mui/material/Typography"; +import { SiteAddress } from "@planx/components/FindProperty/model"; +import { ErrorSummaryContainer } from "@planx/components/shared/Preview/ErrorSummaryContainer"; +import { SchemaFields } from "@planx/components/shared/Schema/SchemaFields"; import { PublicProps } from "@planx/components/ui"; +import { useStore } from "pages/FlowEditor/lib/store"; import React, { useEffect, useRef } from "react"; import { FONT_WEIGHT_SEMI_BOLD } from "theme"; import ErrorWrapper from "ui/shared/ErrorWrapper"; -import InputRow from "ui/shared/InputRow"; import Card from "../../shared/Preview/Card"; import CardHeader from "../../shared/Preview/CardHeader"; -import type { Field, List } from "../model"; +import type { List } from "../model"; import { formatSchemaDisplayValue } from "../utils"; import { ListProvider, useListContext } from "./Context"; -import { - ChecklistFieldInput, - MapFieldInput, - DateFieldInput, - NumberFieldInput, - RadioFieldInput, - SelectFieldInput, - TextFieldInput, -} from "./Fields"; export type Props = PublicProps; @@ -46,36 +40,18 @@ const CardButton = styled(Button)(({ theme }) => ({ gap: theme.spacing(2), })); -/** - * Controller to return correct user input for field in schema - */ -const InputField: React.FC = (props) => { - const inputFieldId = `input-${props.type}-${props.data.fn}`; - - switch (props.type) { - case "text": - return ; - case "number": - return ; - case "question": - if (props.data.options.length === 2) { - return ; - } - return ; - case "checklist": - return ; - case "date": - return ; - case "map": - return ; - } -}; - const ActiveListCard: React.FC<{ index: number; }> = ({ index: i }) => { - const { schema, saveItem, cancelEditItem, errors, isPageComponent } = - useListContext(); + const { + schema, + saveItem, + cancelEditItem, + errors, + isPageComponent, + formik, + activeIndex, + } = useListContext(); const ref = useRef(null); useEffect(() => { @@ -93,11 +69,11 @@ const ActiveListCard: React.FC<{ {schema.type} {!isPageComponent && ` ${i + 1}`} - {schema.fields.map((field, i) => ( - - - - ))} +