From ad816b927645e9033fcb6ea269b3cf41cc3d1efa Mon Sep 17 00:00:00 2001 From: Ian Jones <51156018+ianjon3s@users.noreply.github.com> Date: Fri, 21 Jun 2024 12:13:44 +0100 Subject: [PATCH 1/7] feat: Updated editor teams and team selection pages (#3303) --- .../ui-driven/src/create-flow/helpers.ts | 2 +- e2e/tests/ui-driven/src/login.spec.ts | 6 +- editor.planx.uk/src/pages/Team.tsx | 179 +++++++++--------- editor.planx.uk/src/pages/Teams.tsx | 118 ++++++++---- editor.planx.uk/src/routes/authenticated.tsx | 6 +- editor.planx.uk/src/ui/editor/Dashboard.tsx | 22 +++ 6 files changed, 203 insertions(+), 130 deletions(-) create mode 100644 editor.planx.uk/src/ui/editor/Dashboard.tsx diff --git a/e2e/tests/ui-driven/src/create-flow/helpers.ts b/e2e/tests/ui-driven/src/create-flow/helpers.ts index edaea7b683..842cf6e430 100644 --- a/e2e/tests/ui-driven/src/create-flow/helpers.ts +++ b/e2e/tests/ui-driven/src/create-flow/helpers.ts @@ -29,6 +29,6 @@ export async function getTeamPage({ teamName: string; }): Promise { const page = await getAdminPage({ browser, userId }); - await page.locator("h2", { hasText: teamName }).click(); + await page.locator("h3", { hasText: teamName }).click(); return page; } diff --git a/e2e/tests/ui-driven/src/login.spec.ts b/e2e/tests/ui-driven/src/login.spec.ts index ea7df20615..62481fc98c 100644 --- a/e2e/tests/ui-driven/src/login.spec.ts +++ b/e2e/tests/ui-driven/src/login.spec.ts @@ -37,7 +37,7 @@ test.describe("Login", () => { return response.url().includes("/graphql"); }); - const team = page.locator("h2", { hasText: context.team.name }); + const team = page.locator("h3", { hasText: context.team.name }); await expect(team).toBeVisible(); }); @@ -50,7 +50,7 @@ test.describe("Login", () => { }); await page.goto("/"); - const teamLink = page.locator("h2").filter({ hasText: context.team.name }); + const teamLink = page.locator("h3").filter({ hasText: context.team.name }); await teamLink.waitFor(); // wait for this to be visible // drop graphql requests @@ -67,7 +67,7 @@ test.describe("Login", () => { route.continue(); }); await expect( - page.locator("h1").filter({ hasText: "My services" }), + page.locator("h1").filter({ hasText: "Services" }), ).toBeVisible(); await expect(page.getByText(toastText)).toBeHidden(); }); diff --git a/editor.planx.uk/src/pages/Team.tsx b/editor.planx.uk/src/pages/Team.tsx index d530c235f8..fc6ee62a4c 100644 --- a/editor.planx.uk/src/pages/Team.tsx +++ b/editor.planx.uk/src/pages/Team.tsx @@ -1,10 +1,11 @@ import { gql } from "@apollo/client"; -import Add from "@mui/icons-material/Add"; +import AddCircleOutlineIcon from "@mui/icons-material/AddCircleOutline"; import Edit from "@mui/icons-material/Edit"; import Visibility from "@mui/icons-material/Visibility"; import Box from "@mui/material/Box"; import Button from "@mui/material/Button"; import ButtonBase from "@mui/material/ButtonBase"; +import Container from "@mui/material/Container"; import Dialog from "@mui/material/Dialog"; import DialogActions from "@mui/material/DialogActions"; import DialogContent from "@mui/material/DialogContent"; @@ -12,10 +13,11 @@ import DialogContentText from "@mui/material/DialogContentText"; import DialogTitle from "@mui/material/DialogTitle"; import { styled } from "@mui/material/styles"; import Typography from "@mui/material/Typography"; -import orderBy from "lodash/orderBy"; import React, { useCallback, useEffect, useState } from "react"; import { Link, useNavigation } from "react-navi"; import { FONT_WEIGHT_SEMI_BOLD } from "theme"; +import { borderedFocusStyle } from "theme"; +import Dashboard from "ui/editor/Dashboard"; import { slugify } from "utils"; import { client } from "../lib/graphql"; @@ -24,21 +26,11 @@ import { useStore } from "./FlowEditor/lib/store"; import { formatLastEditMessage } from "./FlowEditor/utils"; const Root = styled(Box)(({ theme }) => ({ - backgroundColor: theme.palette.background.dark, - color: "#fff", + backgroundColor: theme.palette.background.default, width: "100%", - flex: 1, - justifyContent: "flex-start", - alignItems: "center", -})); - -const Dashboard = styled(Box)(({ theme }) => ({ - backgroundColor: theme.palette.background.dark, - color: "#fff", - width: "100%", - maxWidth: 600, - margin: "auto", - padding: theme.spacing(8, 0, 4, 0), + display: "flex", + alignItems: "flex-start", + flexGrow: 1, })); const DashboardList = styled("ul")(({ theme }) => ({ @@ -50,7 +42,13 @@ const DashboardList = styled("ul")(({ theme }) => ({ const DashboardListItem = styled("li")(({ theme }) => ({ listStyle: "none", position: "relative", - padding: theme.spacing(2.5, 2), + color: theme.palette.common.white, + margin: theme.spacing(1, 0), + background: theme.palette.text.primary, + display: "flex", + justifyContent: "space-between", + alignItems: "stretch", + borderRadius: "2px", })); const DashboardLink = styled(Link)(({ theme }) => ({ @@ -59,21 +57,23 @@ const DashboardLink = styled(Link)(({ theme }) => ({ textDecoration: "none", color: "currentColor", fontWeight: FONT_WEIGHT_SEMI_BOLD, - marginBottom: theme.spacing(1.5), - marginTop: 0, + padding: theme.spacing(2), + margin: 0, + width: "100%", + "&:focus-within": { + ...borderedFocusStyle, + }, })); const StyledSimpleMenu = styled(SimpleMenu)(({ theme }) => ({ - position: "absolute", - top: theme.spacing(2), - right: theme.spacing(1), + display: "flex", + borderLeft: `1px solid ${theme.palette.border.main}`, })); -const LinkSubText = styled(Box)(() => ({ - color: "#aaa", - "& a": { - color: "#fff", - }, +const LinkSubText = styled(Box)(({ theme }) => ({ + color: theme.palette.grey[400], + fontWeight: "normal", + paddingTop: "0.5em", })); const Confirm = ({ @@ -113,13 +113,12 @@ const Confirm = ({ ); const AddButtonRoot = styled(ButtonBase)(({ theme }) => ({ - width: "100%", - padding: theme.spacing(4), fontSize: 20, - backgroundColor: "rgba(255,255,255,0.25)", - display: "block", + display: "flex", + alignItems: "center", textAlign: "left", - marginTop: theme.spacing(2), + color: theme.palette.primary.main, + fontWeight: FONT_WEIGHT_SEMI_BOLD, })); function AddButton({ @@ -131,7 +130,7 @@ function AddButton({ }): FCReturn { return ( - {children} + {children} ); } @@ -191,17 +190,17 @@ const FlowItem: React.FC = ({ /> )} - - + + {flow.name} - + {formatLastEditMessage( flow.operations[0].createdAt, flow.operations[0]?.actor, )} - + {useStore.getState().canUserEditTeam(teamSlug) && ( { return ( - - - My services - - {useStore.getState().canUserEditTeam(slug) ? ( - - ) : ( - - )} - - {useStore.getState().canUserEditTeam(slug) && ( - { - const newFlowName = prompt("Service name"); - if (newFlowName) { - const newFlowSlug = slugify(newFlowName); - useStore - .getState() - .createFlow(teamId, newFlowSlug, newFlowName) - .then((newId: string) => { - navigation.navigate(`/${slug}/${newId}`); - }); - } + + - Add a new service - - )} - {flows && ( - - {flows.map((flow: any) => ( - { - fetchFlows(); + + + Services + + {useStore.getState().canUserEditTeam(slug) ? ( + + ) : ( + + )} + + {useStore.getState().canUserEditTeam(slug) && ( + { + const newFlowName = prompt("Service name"); + if (newFlowName) { + const newFlowSlug = slugify(newFlowName); + useStore + .getState() + .createFlow(teamId, newFlowSlug, newFlowName) + .then((newId: string) => { + navigation.navigate(`/${slug}/${newId}`); + }); + } }} - /> - ))} - - )} + > + Add a new service + + )} + + {flows && ( + + {flows.map((flow: any) => ( + { + fetchFlows(); + }} + /> + ))} + + )} + ); diff --git a/editor.planx.uk/src/pages/Teams.tsx b/editor.planx.uk/src/pages/Teams.tsx index ea11e0d1b1..f8208dd2c9 100644 --- a/editor.planx.uk/src/pages/Teams.tsx +++ b/editor.planx.uk/src/pages/Teams.tsx @@ -1,71 +1,109 @@ -import Edit from "@mui/icons-material/Edit"; -import Visibility from "@mui/icons-material/Visibility"; import Box from "@mui/material/Box"; import Card from "@mui/material/Card"; +import Container from "@mui/material/Container"; import { styled } from "@mui/material/styles"; import Typography from "@mui/material/Typography"; import { Team } from "@opensystemslab/planx-core/types"; import React from "react"; import { Link } from "react-navi"; +import { borderedFocusStyle } from "theme"; +import Dashboard from "ui/editor/Dashboard"; import { useStore } from "./FlowEditor/lib/store"; +interface TeamTheme { + slug: string; + primaryColour: string; +} + interface Props { teams: Array; + teamTheme: Array; } -const Root = styled(Box)(({ theme }) => ({ - backgroundColor: theme.palette.background.dark, - color: "#fff", +export const Root = styled(Box)(({ theme }) => ({ + backgroundColor: theme.palette.background.default, width: "100%", - flex: 1, + display: "flex", + alignItems: "flex-start", + flexGrow: 1, +})); + +const StyledLink = styled(Link)(() => ({ + textDecoration: "none", + "&:focus-within > div": { + ...borderedFocusStyle, + }, +})); + +const TeamCard = styled(Card)(({ theme }) => ({ + display: "flex", justifyContent: "flex-start", alignItems: "center", + marginBottom: theme.spacing(2), + color: theme.palette.text.primary, + outline: `1px solid ${theme.palette.border.light}`, + outlineOffset: "-1px", + borderRadius: "1px", })); -const Dashboard = styled(Box)(({ theme }) => ({ - backgroundColor: theme.palette.background.dark, - color: "#fff", - width: "100%", - maxWidth: 600, - margin: "auto", - padding: theme.spacing(8, 0, 4, 0), +const TeamColourBand = styled(Box)(({ theme }) => ({ + display: "flex", + alignSelf: "stretch", + width: theme.spacing(1.5), + zIndex: 1, })); -const StyledLink = styled(Link)(() => ({ - textDecoration: "none", -})); +const Teams: React.FC = ({ teams, teamTheme }) => { + const canUserEditTeam = useStore.getState().canUserEditTeam; + + const editableTeams: Team[] = []; + const viewOnlyTeams: Team[] = []; -const Teams: React.FC = ({ teams }) => { + teams.forEach((team) => + canUserEditTeam(team.slug) + ? editableTeams.push(team) + : viewOnlyTeams.push(team), + ); + + const renderTeams = (teamsToRender: Array) => + teamsToRender.map((team) => { + return ( + + + + + {team.name} + + + + ); + }); return ( - - + + Select a team - - {teams.map(({ name, slug }) => ( - - - - {name} + {editableTeams.length > 0 && ( + <> + + My teams + + {renderTeams(editableTeams)} + + )} + + {viewOnlyTeams.length > 0 && ( + <> + + Other teams (view only) - {useStore.getState().canUserEditTeam(slug) ? ( - - ) : ( - - )} - - - ))} + {renderTeams(viewOnlyTeams)} + + )} + ); diff --git a/editor.planx.uk/src/routes/authenticated.tsx b/editor.planx.uk/src/routes/authenticated.tsx index 659b907d79..b6a4745a52 100644 --- a/editor.planx.uk/src/routes/authenticated.tsx +++ b/editor.planx.uk/src/routes/authenticated.tsx @@ -30,6 +30,10 @@ const editorRoutes = compose( id name slug + theme { + primaryColour: primary_colour + logo + } } } `, @@ -39,7 +43,7 @@ const editorRoutes = compose( return { title: makeTitle("Teams"), - view: , + view: , }; }), diff --git a/editor.planx.uk/src/ui/editor/Dashboard.tsx b/editor.planx.uk/src/ui/editor/Dashboard.tsx new file mode 100644 index 0000000000..a18472da46 --- /dev/null +++ b/editor.planx.uk/src/ui/editor/Dashboard.tsx @@ -0,0 +1,22 @@ +import Box from "@mui/material/Box"; +import { containerClasses } from "@mui/material/Container"; +import { styled } from "@mui/material/styles"; +import { HEADER_HEIGHT } from "components/Header"; +import React, { PropsWithChildren } from "react"; + +const Root = styled(Box)(({ theme }) => ({ + backgroundColor: theme.palette.background.default, + color: theme.palette.text.primary, + display: "flex", + flexDirection: "row", + width: "100%", + minHeight: `calc(100vh - ${HEADER_HEIGHT}px)`, + [`& > .${containerClasses.root}`]: { + paddingTop: theme.spacing(6), + paddingBottom: theme.spacing(6), + }, +})); + +export default function Dashboard(props: PropsWithChildren) { + return {props.children}; +} From 81ba8f92b37bfd639dfbd11a91bfcbd6abd72938 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Mon, 24 Jun 2024 11:49:06 +0100 Subject: [PATCH 2/7] test: Update regression tests for new Team page layout (#3312) --- e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts index 7d86cb19a9..3948a7338f 100644 --- a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts +++ b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts @@ -47,7 +47,7 @@ test.describe("Navigation", () => { Promise.all([await page.goto("/"), await initialRequest]); - const team = page.locator("h2", { hasText: context.team.name }); + const team = page.locator("h3", { hasText: context.team.name }); let isRepeatedRequestMade = false; page.on( @@ -74,7 +74,7 @@ test.describe("Navigation", () => { }); await page.goto("/"); - const team = page.locator("h2", { hasText: context.team.name }); + const team = page.locator("h3", { hasText: context.team.name }); await team.click(); const teamSlugInHeader = page.getByRole("link", { From 8ede6d0b350fca99f794395835cf90dbb2d2128c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Tue, 25 Jun 2024 10:10:14 +0100 Subject: [PATCH 3/7] fix: Update "cancel" functionality for List component (#3311) --- .../@planx/components/List/Public/Context.tsx | 44 ++++++++++++++++--- .../components/List/Public/index.test.tsx | 39 +++++++++++++++- .../@planx/components/List/Public/index.tsx | 16 +++---- 3 files changed, 80 insertions(+), 19 deletions(-) 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 68920e56b2..99af3801fb 100644 --- a/editor.planx.uk/src/@planx/components/List/Public/Context.tsx +++ b/editor.planx.uk/src/@planx/components/List/Public/Context.tsx @@ -17,6 +17,7 @@ import { List, Schema, UserData, + UserResponse, } from "../model"; import { flatten, @@ -35,6 +36,14 @@ interface ListContextValue { formik: FormikProps; validateAndSubmitForm: () => void; listProps: PublicProps; + /** + * @deprecated + * @description + * Hide features if the schema is temporarily mocking a "Page" component + * @todo + * Refactor and allow a single-item "Page" component to properly manage this + */ + isPageComponent: boolean; errors: { addItem: boolean; unsavedItem: boolean; @@ -54,6 +63,8 @@ export const ListProvider: React.FC = (props) => { props.previouslySubmittedData ? -1 : 0, ); + const [activeItemInitialState, setActiveItemInitialState] = useState(undefined); + const [addItemError, setAddItemError] = useState(false); const [unsavedItemError, setUnsavedItemError] = useState(false); const [minError, setMinError] = useState(false); @@ -83,7 +94,7 @@ export const ListProvider: React.FC = (props) => { const errors = await formik.validateForm(); const isValid = !errors.userData?.length; if (isValid) { - setActiveIndex(-1); + exitEditMode(); setAddItemError(false); } }; @@ -91,12 +102,9 @@ export const ListProvider: React.FC = (props) => { const removeItem = (index: number) => { resetErrors(); + // If item is before currently active card, retain active card if (activeIndex && index < activeIndex) { - // If item is before currently active card, retain active card setActiveIndex((prev) => (prev === -1 ? 0 : prev - 1)); - } else if (index === activeIndex || index === 0) { - // If item is currently in Edit mode, exit Edit mode - cancelEditItem(); } // Remove item from userData @@ -120,9 +128,20 @@ export const ListProvider: React.FC = (props) => { formik.handleSubmit(); }; - const cancelEditItem = () => setActiveIndex(-1); + const cancelEditItem = () => { + activeItemInitialState + ? resetItemToPreviousState() + : removeItem(activeIndex); + + setActiveItemInitialState(undefined); + + exitEditMode(); + } - const editItem = (index: number) => setActiveIndex(index); + const editItem = (index: number) => { + setActiveItemInitialState(formik.values.userData[index]); + setActiveIndex(index); + } const getInitialValues = () => { const previousValues = getPreviouslySubmittedData(props); @@ -131,6 +150,16 @@ export const ListProvider: React.FC = (props) => { return schema.min ? [generateInitialValues(schema)] : []; }; + const exitEditMode = () => setActiveIndex(-1); + + const resetItemToPreviousState = () => + formik.setFieldValue( + `userData[${activeIndex}]`, + activeItemInitialState + ) + + const isPageComponent = schema.max === 1; + const formik = useFormik({ initialValues: { userData: getInitialValues(), @@ -189,6 +218,7 @@ export const ListProvider: React.FC = (props) => { cancelEditItem, formik, validateAndSubmitForm, + isPageComponent, errors: { addItem: addItemError, unsavedItem: unsavedItemError, diff --git a/editor.planx.uk/src/@planx/components/List/Public/index.test.tsx b/editor.planx.uk/src/@planx/components/List/Public/index.test.tsx index ce4cfad6b8..1f25e0eb34 100644 --- a/editor.planx.uk/src/@planx/components/List/Public/index.test.tsx +++ b/editor.planx.uk/src/@planx/components/List/Public/index.test.tsx @@ -338,8 +338,45 @@ describe("Building a list", () => { within(cards[0]!).getByLabelText(/What's their name?/), ).toBeInTheDocument(); }); -}); + test("Cancelling an invalid (new) item removes it", async () => { + const { getAllByTestId, getByText, user, queryAllByTestId } = setup( + , + ); + + let cards = getAllByTestId(/list-card/); + expect(cards).toHaveLength(1); + + const cancelButton = getByText(/Cancel/, { selector: "button" }); + await user.click(cancelButton) + + cards = queryAllByTestId(/list-card/); + expect(cards).toHaveLength(0); + }); + + test("Cancelling a valid (existing) item resets previous state", async () => { + const { getByLabelText, getByText, user, queryByText } = setup( + , + ); + + await fillInResponse(user); + + expect(getByText("richard.parker@pi.com")).toBeInTheDocument(); + + const editButton = getByText(/Edit/, { selector: "button" }); + await user.click(editButton); + + const emailInput = getByLabelText(/email/); + await user.type(emailInput, "my.new.email@test.com"); + + const cancelButton = getByText(/Cancel/, { selector: "button" }); + await user.click(cancelButton); + + expect(queryByText("my.new.email@test.com")).not.toBeInTheDocument(); + expect(getByText("richard.parker@pi.com")).toBeInTheDocument(); + }); +}); + describe("Form validation and error handling", () => { test.todo("form validation is triggered when saving an item"); test.todo("text fields use existing validation schemas"); 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 50d49140cb..1afd2c3ecd 100644 --- a/editor.planx.uk/src/@planx/components/List/Public/index.tsx +++ b/editor.planx.uk/src/@planx/components/List/Public/index.tsx @@ -65,10 +65,7 @@ const InputField: React.FC = (props) => { const ActiveListCard: React.FC<{ index: number; }> = ({ index: i }) => { - const { schema, saveItem, cancelEditItem, errors } = useListContext(); - - // Hide the index number in the card title if the schema has a max length of 1 - const shouldShowIndexTitle = schema.max !== 1; + const { schema, saveItem, cancelEditItem, errors, isPageComponent } = useListContext(); return ( {schema.type} - {shouldShowIndexTitle && ` ${i + 1}`} + {!isPageComponent && ` ${i + 1}`} {schema.fields.map((field, i) => ( @@ -92,7 +89,7 @@ const ActiveListCard: React.FC<{ > Save - + {!isPageComponent && } @@ -102,16 +99,13 @@ const ActiveListCard: React.FC<{ const InactiveListCard: React.FC<{ index: number; }> = ({ index: i }) => { - const { schema, formik, removeItem, editItem } = useListContext(); - - // Hide the index number in the card title if the schema has a max length of 1 - const shouldShowIndexTitle = schema.max !== 1; + const { schema, formik, removeItem, editItem, isPageComponent } = useListContext(); return ( {schema.type} - {shouldShowIndexTitle && ` ${i + 1}`} + {!isPageComponent && ` ${i + 1}`} From 9020589ea48c4eed55bbd3f8238d3b7d1217e906 Mon Sep 17 00:00:00 2001 From: Rory Doak <138574807+RODO94@users.noreply.github.com> Date: Tue, 25 Jun 2024 10:14:08 +0100 Subject: [PATCH 4/7] feat: Team Settings Editor Form UI Changes (#3305) --- .../Settings/GeneralSettings/BoundaryForm.tsx | 59 ++++++++++ .../Settings/GeneralSettings/ContactForm.tsx | 95 ++++++++++++++++ .../Settings/GeneralSettings/index.tsx | 103 ++++++++++++++++++ .../Settings/shared/SettingsForm.tsx | 2 +- editor.planx.uk/src/routes/teamSettings.tsx | 7 ++ 5 files changed, 265 insertions(+), 1 deletion(-) create mode 100644 editor.planx.uk/src/pages/FlowEditor/components/Settings/GeneralSettings/BoundaryForm.tsx create mode 100644 editor.planx.uk/src/pages/FlowEditor/components/Settings/GeneralSettings/ContactForm.tsx create mode 100644 editor.planx.uk/src/pages/FlowEditor/components/Settings/GeneralSettings/index.tsx diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Settings/GeneralSettings/BoundaryForm.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Settings/GeneralSettings/BoundaryForm.tsx new file mode 100644 index 0000000000..e8a249ad6f --- /dev/null +++ b/editor.planx.uk/src/pages/FlowEditor/components/Settings/GeneralSettings/BoundaryForm.tsx @@ -0,0 +1,59 @@ +import { useFormik } from "formik"; +import React, { ChangeEvent } from "react"; +import InputDescription from "ui/editor/InputDescription"; +import Input from "ui/shared/Input"; +import InputRow from "ui/shared/InputRow"; +import InputRowLabel from "ui/shared/InputRowLabel"; + +import { SettingsForm } from "../shared/SettingsForm"; +import { FormProps } from "."; + +export default function BoundaryForm({ formikConfig, onSuccess }: FormProps) { + const formik = useFormik({ + ...formikConfig, + onSubmit(values, { resetForm }) { + onSuccess(); + resetForm({ values }); + }, + }); + + return ( + + The boundary URL is used to retrieve the outer boundary of your + council area. This can then help users define whether they are within + your council area. +
+
+ The boundary should be given as a link from:{" "} + + https://www.planning.data.gov.uk/ + + + } + input={ + <> + + + Boundary URL + ) => { + formik.setFieldValue("boundaryUrl", ev.target.value); + }} + /> + + + + } + /> + ); +} diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Settings/GeneralSettings/ContactForm.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Settings/GeneralSettings/ContactForm.tsx new file mode 100644 index 0000000000..24075a8168 --- /dev/null +++ b/editor.planx.uk/src/pages/FlowEditor/components/Settings/GeneralSettings/ContactForm.tsx @@ -0,0 +1,95 @@ +import { useFormik } from "formik"; +import React, { ChangeEvent } from "react"; +import InputDescription from "ui/editor/InputDescription"; +import Input from "ui/shared/Input"; +import InputRow from "ui/shared/InputRow"; +import InputRowLabel from "ui/shared/InputRowLabel"; +import * as Yup from "yup"; + +import { SettingsForm } from "../shared/SettingsForm"; +import { FormProps } from "."; + +export default function ContactForm({ formikConfig, onSuccess }: FormProps) { + const formSchema = Yup.object().shape({ + helpEmail: Yup.string() + .email("Please enter valid email") + .required("Help Email is required"), + helpPhone: Yup.string().required("Help Phone is required"), + helpOpeningHours: Yup.string(), + homepage: Yup.string().url("Please enter a valid URL for the homepage"), + }); + + const formik = useFormik({ + ...formikConfig, + validationSchema: formSchema, + onSubmit(values, { resetForm }) { + onSuccess(); + resetForm({ values }); + }, + }); + + const onChangeFn = (type: string, event: ChangeEvent) => + formik.setFieldValue(type, event.target.value); + + return ( + + Details to help direct different messages, feedback, and enquiries + from users. + + } + input={ + <> + + + Homepage URL + { + onChangeFn("homepage", event); + }} + /> + + + + + Contact email address + { + onChangeFn("helpEmail", event); + }} + /> + + + + + Phone number + { + onChangeFn("helpPhone", event); + }} + /> + + + + + Opening hours + { + onChangeFn("helpOpeningHours", event); + }} + /> + + + + } + /> + ); +} diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Settings/GeneralSettings/index.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Settings/GeneralSettings/index.tsx new file mode 100644 index 0000000000..7ce9edd670 --- /dev/null +++ b/editor.planx.uk/src/pages/FlowEditor/components/Settings/GeneralSettings/index.tsx @@ -0,0 +1,103 @@ +import Alert from "@mui/material/Alert"; +import Box from "@mui/material/Box"; +import Snackbar from "@mui/material/Snackbar"; +import Typography from "@mui/material/Typography"; +import { FormikConfig } from "formik"; +import React, { useEffect, useState } from "react"; +import EditorRow from "ui/editor/EditorRow"; + +import BoundaryForm from "./BoundaryForm"; +import ContactForm from "./ContactForm"; + +export interface GeneralSettings { + boundaryUrl: string; + helpEmail: string; + helpPhone: string; + helpOpeningHours: string; + homepage: string; + isPlanningDataCollected: boolean; + portalName: string; + portalUrl: string; +} + +export interface FormProps { + formikConfig: FormikConfig; + onSuccess: () => void; +} + +const GeneralSettings: React.FC = () => { + const [formikConfig, setFormikConfig] = useState< + FormikConfig | undefined + >(undefined); + + const initialValues = { + boundaryUrl: "", + helpEmail: "", + helpPhone: "", + helpOpeningHours: "", + homepage: "", + isPlanningDataCollected: true, + portalName: "", + portalUrl: "", + }; + + useEffect(() => { + const fetchTeam = async () => { + try { + setFormikConfig({ + initialValues: initialValues, + onSubmit: () => {}, + validateOnBlur: false, + validateOnChange: false, + enableReinitialize: true, + }); + } catch (error) { + console.error("Error fetching team:", error); + } + }; + + fetchTeam(); + }, []); + + const [open, setOpen] = useState(false); + const [updateMessage, setUpdateMessage] = useState("Setting Updated"); + + const handleClose = ( + _event?: React.SyntheticEvent | Event, + reason?: string, + ) => { + if (reason === "clickaway") { + return; + } + + setOpen(false); + }; + + const onSuccess = () => setOpen(true); + + return ( + + + + General + + + Important links and settings for how your users connect with you + + + {formikConfig && ( + <> + + + + )} + + + {updateMessage} + + + + ); +}; + +export default GeneralSettings; diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Settings/shared/SettingsForm.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Settings/shared/SettingsForm.tsx index 99e001d097..3bb553695e 100644 --- a/editor.planx.uk/src/pages/FlowEditor/components/Settings/shared/SettingsForm.tsx +++ b/editor.planx.uk/src/pages/FlowEditor/components/Settings/shared/SettingsForm.tsx @@ -62,4 +62,4 @@ export const SettingsForm = ({ ); -}; \ No newline at end of file +}; diff --git a/editor.planx.uk/src/routes/teamSettings.tsx b/editor.planx.uk/src/routes/teamSettings.tsx index a21bd4e25a..587160a6ea 100644 --- a/editor.planx.uk/src/routes/teamSettings.tsx +++ b/editor.planx.uk/src/routes/teamSettings.tsx @@ -8,6 +8,7 @@ import { withData, } from "navi"; import DesignSettings from "pages/FlowEditor/components/Settings/DesignSettings"; +import GeneralSettings from "pages/FlowEditor/components/Settings/GeneralSettings"; import TeamSettings from "pages/FlowEditor/components/Settings/TeamSettings"; import { useStore } from "pages/FlowEditor/lib/store"; import React from "react"; @@ -24,6 +25,7 @@ const teamSettingsRoutes = compose( "/": redirect("./team"), "/:tab": map(async (req) => { const isAuthorised = useStore.getState().canUserEditTeam(req.params.team); + if (!isAuthorised) throw new NotFoundError( `User does not have access to ${req.originalUrl}`, @@ -45,6 +47,11 @@ const teamSettingsRoutes = compose( route: "design", Component: DesignSettings, }, + { + name: "General", + route: "general", + Component: GeneralSettings, + }, ]} /> ), From 2991b92b6e4a9ed37cbb697397ea262da533cf0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Tue, 25 Jun 2024 11:11:01 +0100 Subject: [PATCH 5/7] chore: Run `pnpm lint:fix` in Editor (#3313) --- .../@planx/components/DateInput/model.test.ts | 13 +- .../src/@planx/components/DateInput/model.ts | 98 +++++------ .../src/@planx/components/List/Editor.tsx | 2 +- .../@planx/components/List/Public/Context.tsx | 19 +-- .../components/List/Public/index.test.tsx | 4 +- .../@planx/components/List/Public/index.tsx | 6 +- .../components/List/schemas/Floorspace.ts | 61 +++++-- .../List/schemas/ResidentialUnits/Proposed.ts | 13 +- .../components/NumberInput/Public.test.tsx | 26 ++- .../@planx/components/SetValue/utils.test.ts | 160 ++++++++++-------- .../src/@planx/components/SetValue/utils.ts | 1 + .../@planx/components/shared/Buttons/Tag.tsx | 4 +- .../components/shared/Radio/BasicRadio.tsx | 7 +- .../components/AnalyticsDisabledBanner.tsx | 4 +- .../Feedback/FeedbackForm.stories.tsx | 2 +- editor.planx.uk/src/lib/feedback.ts | 2 +- .../Settings/DesignSettings/ButtonForm.tsx | 2 +- .../Settings/DesignSettings/FaviconForm.tsx | 2 +- .../Settings/DesignSettings/TextLinkForm.tsx | 2 +- .../DesignSettings/ThemeAndLogoForm.tsx | 2 +- .../components/Sidebar/EditHistory.tsx | 2 +- .../components/Sidebar/PublishDialog.tsx | 55 ++++-- .../FlowEditor/components/Sidebar/index.tsx | 43 +++-- .../src/pages/FlowEditor/lib/store/preview.ts | 4 +- editor.planx.uk/src/pages/FlowEditor/utils.ts | 2 +- .../src/pages/PlatformAdminPanel.tsx | 10 +- .../src/pages/layout/PublicLayout.tsx | 8 +- editor.planx.uk/src/themeOverrides.d.ts | 2 +- editor.planx.uk/src/ui/shared/Checkbox.tsx | 9 +- 29 files changed, 343 insertions(+), 222 deletions(-) diff --git a/editor.planx.uk/src/@planx/components/DateInput/model.test.ts b/editor.planx.uk/src/@planx/components/DateInput/model.test.ts index 03d7b20439..7d73193590 100644 --- a/editor.planx.uk/src/@planx/components/DateInput/model.test.ts +++ b/editor.planx.uk/src/@planx/components/DateInput/model.test.ts @@ -47,7 +47,10 @@ describe("dateSchema", () => { expect(await dateSchema().isValid("2021-23-03")).toBe(false); }); - const validate = async (date?: string) => await dateSchema().validate(date).catch((err) => err.errors); + const validate = async (date?: string) => + await dateSchema() + .validate(date) + .catch((err) => err.errors); it("throws an error for an undefined value (empty form)", async () => { const errors = await validate(undefined); @@ -58,12 +61,12 @@ describe("dateSchema", () => { const errors = await validate("ab-cd-efgh"); expect(errors[0]).toMatch(/Date must include a day/); }); - + it("throws an error for a missing day", async () => { const errors = await validate("2024-12-"); expect(errors[0]).toMatch(/Date must include a day/); }); - + it("throws an error for a missing month", async () => { const errors = await validate("2024--25"); expect(errors[0]).toMatch(/Date must include a month/); @@ -112,7 +115,7 @@ describe("dateRangeSchema", () => { "1980-06-15", ), ).toBe(false); - }) + }); }); test("padding on input", () => { @@ -145,4 +148,4 @@ test("padding on blur", () => { // Leaves single 0 alone expect(paddedDate("2021-0-2", "blur")).toBe("2021-0-02"); expect(paddedDate("2021-10-0", "blur")).toBe("2021-10-0"); -}); \ No newline at end of file +}); diff --git a/editor.planx.uk/src/@planx/components/DateInput/model.ts b/editor.planx.uk/src/@planx/components/DateInput/model.ts index 4b6ee7b2b5..f661f803e3 100644 --- a/editor.planx.uk/src/@planx/components/DateInput/model.ts +++ b/editor.planx.uk/src/@planx/components/DateInput/model.ts @@ -60,47 +60,33 @@ const displayDate = (date: string): string | undefined => { }; export const parseDate = (date?: string) => { - const [year, month, day] = date?.split("-").map((val) => parseInt(val) || undefined) || []; + const [year, month, day] = + date?.split("-").map((val) => parseInt(val) || undefined) || []; return { year, month, day }; -} +}; export const dateSchema = () => { return string() - .test( - "missing day", - "Date must include a day", - (date?: string) => { - const { day } = parseDate(date); - return day !== undefined; - }) - .test( - "missing month", - "Date must include a month", - (date?: string) => { - const { month } = parseDate(date); - return month !== undefined; - }) - .test( - "missing year", - "Date must include a year", - (date?: string) => { - const { year } = parseDate(date); - return year !== undefined; - }) - .test( - "invalid day", - "Day must be valid", - (date?: string) => { - const { day } = parseDate(date); - return Boolean(day && day <= 31) - }) - .test( - "invalid month", - "Month must be valid", - (date?: string) => { - const { month } = parseDate(date); - return Boolean(month && month <= 12); - }) + .test("missing day", "Date must include a day", (date?: string) => { + const { day } = parseDate(date); + return day !== undefined; + }) + .test("missing month", "Date must include a month", (date?: string) => { + const { month } = parseDate(date); + return month !== undefined; + }) + .test("missing year", "Date must include a year", (date?: string) => { + const { year } = parseDate(date); + return year !== undefined; + }) + .test("invalid day", "Day must be valid", (date?: string) => { + const { day } = parseDate(date); + return Boolean(day && day <= 31); + }) + .test("invalid month", "Month must be valid", (date?: string) => { + const { month } = parseDate(date); + return Boolean(month && month <= 12); + }) .test( "valid", "Enter a valid date in DD.MM.YYYY format", @@ -115,24 +101,26 @@ export const dateRangeSchema: (params: { min?: string; max?: string; }) => SchemaOf = (params) => - dateSchema() - .required("Enter a valid date in DD.MM.YYYY format") - .test({ - name: "too soon", - message: `Enter a date later than ${params.min && displayDate(params.min) - }`, - test: (date: string | undefined) => { - return Boolean(date && !(params.min && date < params.min)); - }, - }) - .test({ - name: "too late", - message: `Enter a date earlier than ${params.max && displayDate(params.max) - }`, - test: (date: string | undefined) => { - return Boolean(date && !(params.max && date > params.max)); - }, - }); + dateSchema() + .required("Enter a valid date in DD.MM.YYYY format") + .test({ + name: "too soon", + message: `Enter a date later than ${ + params.min && displayDate(params.min) + }`, + test: (date: string | undefined) => { + return Boolean(date && !(params.min && date < params.min)); + }, + }) + .test({ + name: "too late", + message: `Enter a date earlier than ${ + params.max && displayDate(params.max) + }`, + test: (date: string | undefined) => { + return Boolean(date && !(params.max && date > params.max)); + }, + }); export const parseDateInput = ( data: Record | undefined, diff --git a/editor.planx.uk/src/@planx/components/List/Editor.tsx b/editor.planx.uk/src/@planx/components/List/Editor.tsx index b62e8daa30..6b85d49b87 100644 --- a/editor.planx.uk/src/@planx/components/List/Editor.tsx +++ b/editor.planx.uk/src/@planx/components/List/Editor.tsx @@ -14,6 +14,7 @@ import InputRowLabel from "ui/shared/InputRowLabel"; import { EditorProps, ICONS, InternalNotes, MoreInformation } from "../ui"; import { List, parseContent } from "./model"; import { ProposedAdvertisements } from "./schemas/Adverts"; +import { NonResidentialFloorspace } from "./schemas/Floorspace"; import { BuildingDetailsGLA } from "./schemas/GLA/BuildingDetails"; import { CommunalSpaceGLA } from "./schemas/GLA/CommunalSpace"; import { ExistingAndProposedUsesGLA } from "./schemas/GLA/ExistingAndProposedUses"; @@ -24,7 +25,6 @@ import { ResidentialUnitsExisting } from "./schemas/ResidentialUnits/Existing"; import { ResidentialUnitsGLAGained } from "./schemas/ResidentialUnits/GLA/Gained"; import { ResidentialUnitsGLALost } from "./schemas/ResidentialUnits/GLA/Lost"; import { ResidentialUnitsProposed } from "./schemas/ResidentialUnits/Proposed"; -import { NonResidentialFloorspace } from "./schemas/Floorspace"; type Props = EditorProps; 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 99af3801fb..e9c22cee1a 100644 --- a/editor.planx.uk/src/@planx/components/List/Public/Context.tsx +++ b/editor.planx.uk/src/@planx/components/List/Public/Context.tsx @@ -63,7 +63,9 @@ export const ListProvider: React.FC = (props) => { props.previouslySubmittedData ? -1 : 0, ); - const [activeItemInitialState, setActiveItemInitialState] = useState(undefined); + const [activeItemInitialState, setActiveItemInitialState] = useState< + UserResponse | undefined + >(undefined); const [addItemError, setAddItemError] = useState(false); const [unsavedItemError, setUnsavedItemError] = useState(false); @@ -132,16 +134,16 @@ export const ListProvider: React.FC = (props) => { activeItemInitialState ? resetItemToPreviousState() : removeItem(activeIndex); - + setActiveItemInitialState(undefined); exitEditMode(); - } + }; const editItem = (index: number) => { setActiveItemInitialState(formik.values.userData[index]); setActiveIndex(index); - } + }; const getInitialValues = () => { const previousValues = getPreviouslySubmittedData(props); @@ -151,12 +153,9 @@ export const ListProvider: React.FC = (props) => { }; const exitEditMode = () => setActiveIndex(-1); - - const resetItemToPreviousState = () => - formik.setFieldValue( - `userData[${activeIndex}]`, - activeItemInitialState - ) + + const resetItemToPreviousState = () => + formik.setFieldValue(`userData[${activeIndex}]`, activeItemInitialState); const isPageComponent = schema.max === 1; diff --git a/editor.planx.uk/src/@planx/components/List/Public/index.test.tsx b/editor.planx.uk/src/@planx/components/List/Public/index.test.tsx index 1f25e0eb34..aa47784682 100644 --- a/editor.planx.uk/src/@planx/components/List/Public/index.test.tsx +++ b/editor.planx.uk/src/@planx/components/List/Public/index.test.tsx @@ -348,7 +348,7 @@ describe("Building a list", () => { expect(cards).toHaveLength(1); const cancelButton = getByText(/Cancel/, { selector: "button" }); - await user.click(cancelButton) + await user.click(cancelButton); cards = queryAllByTestId(/list-card/); expect(cards).toHaveLength(0); @@ -376,7 +376,7 @@ describe("Building a list", () => { expect(getByText("richard.parker@pi.com")).toBeInTheDocument(); }); }); - + describe("Form validation and error handling", () => { test.todo("form validation is triggered when saving an item"); test.todo("text fields use existing validation schemas"); 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 1afd2c3ecd..44117493dc 100644 --- a/editor.planx.uk/src/@planx/components/List/Public/index.tsx +++ b/editor.planx.uk/src/@planx/components/List/Public/index.tsx @@ -65,7 +65,8 @@ const InputField: React.FC = (props) => { const ActiveListCard: React.FC<{ index: number; }> = ({ index: i }) => { - const { schema, saveItem, cancelEditItem, errors, isPageComponent } = useListContext(); + const { schema, saveItem, cancelEditItem, errors, isPageComponent } = + useListContext(); return ( = ({ index: i }) => { - const { schema, formik, removeItem, editItem, isPageComponent } = useListContext(); + const { schema, formik, removeItem, editItem, isPageComponent } = + useListContext(); return ( diff --git a/editor.planx.uk/src/@planx/components/List/schemas/Floorspace.ts b/editor.planx.uk/src/@planx/components/List/schemas/Floorspace.ts index e0329432c3..f8476d0e3f 100644 --- a/editor.planx.uk/src/@planx/components/List/schemas/Floorspace.ts +++ b/editor.planx.uk/src/@planx/components/List/schemas/Floorspace.ts @@ -28,15 +28,24 @@ export const NonResidentialFloorspace: Schema = { }, { id: "eAShops", - data: { text: "E(a) - Retail (other than hot food): Shops", val: "eAShops" }, + data: { + text: "E(a) - Retail (other than hot food): Shops", + val: "eAShops", + }, }, { id: "eANetTradeableArea", - data: { text: "E(a) - Retail (other than hot food): Net tradeable area", val: "eANetTradeableArea" }, + data: { + text: "E(a) - Retail (other than hot food): Net tradeable area", + val: "eANetTradeableArea", + }, }, { id: "eB", - data: { text: "E(b) - Sale of food and drink (mostly consumed on the premises)", val: "eB" }, + data: { + text: "E(b) - Sale of food and drink (mostly consumed on the premises)", + val: "eB", + }, }, { id: "eCI", @@ -44,7 +53,10 @@ export const NonResidentialFloorspace: Schema = { }, { id: "eCII", - data: { text: "E(c)(ii) - Professional services (other than health or medical)", val: "eCII" }, + data: { + text: "E(c)(ii) - Professional services (other than health or medical)", + val: "eCII", + }, }, { id: "eCIII", @@ -52,7 +64,10 @@ export const NonResidentialFloorspace: Schema = { }, { id: "eD", - data: { text: "E(d) - Indoor sports, recreation or fitness", val: "eD" }, + data: { + text: "E(d) - Indoor sports, recreation or fitness", + val: "eD", + }, }, { id: "eF", @@ -60,15 +75,24 @@ export const NonResidentialFloorspace: Schema = { }, { id: "eGI", - data: { text: "E(g)(i) - Office (to carry out operational or administrative functions)", val: "eGI" }, + data: { + text: "E(g)(i) - Office (to carry out operational or administrative functions)", + val: "eGI", + }, }, { id: "eGII", - data: { text: "E(g)(ii) - Research and development of products or processes", val: "eGII" }, + data: { + text: "E(g)(ii) - Research and development of products or processes", + val: "eGII", + }, }, { id: "eGIII", - data: { text: "E(g)(iii) - Any industrial process (can be carried out within a residential area)", val: "eGIII" }, + data: { + text: "E(g)(iii) - Any industrial process (can be carried out within a residential area)", + val: "eGIII", + }, }, { id: "fOneA", @@ -121,11 +145,17 @@ export const NonResidentialFloorspace: Schema = { }, { id: "fTwoA", - data: { text: "F2(a) - Shop selling essential goods (not over 280sqm and no other such facility in 1000m radius)", val: "fTwoA" }, + data: { + text: "F2(a) - Shop selling essential goods (not over 280sqm and no other such facility in 1000m radius)", + val: "fTwoA", + }, }, { id: "fTwoB", - data: { text: "F2(b) - Hall or meeting place for local community (principal use)", val: "fTwoB" }, + data: { + text: "F2(b) - Hall or meeting place for local community (principal use)", + val: "fTwoB", + }, }, { id: "fTwoC", @@ -133,7 +163,10 @@ export const NonResidentialFloorspace: Schema = { }, { id: "fTwoD", - data: { text: "F2(d) - Indoor or outdoor swimming pool or skating rink", val: "fTwoD" }, + data: { + text: "F2(d) - Indoor or outdoor swimming pool or skating rink", + val: "fTwoD", + }, }, { id: "other", data: { text: "Other", val: "other" } }, ], @@ -151,7 +184,8 @@ export const NonResidentialFloorspace: Schema = { { type: "number", data: { - title: "What is the gross internal floor area to be lost by change of use or demolition?", + title: + "What is the gross internal floor area to be lost by change of use or demolition?", units: "m²", fn: "area.loss", allowNegatives: false, @@ -160,7 +194,8 @@ export const NonResidentialFloorspace: Schema = { { type: "number", data: { - title: "What is the total gross internal floor area proposed (including change of use)?", + title: + "What is the total gross internal floor area proposed (including change of use)?", units: "m²", fn: "area.proposed", allowNegatives: false, diff --git a/editor.planx.uk/src/@planx/components/List/schemas/ResidentialUnits/Proposed.ts b/editor.planx.uk/src/@planx/components/List/schemas/ResidentialUnits/Proposed.ts index 77b533791d..ea99c599b0 100644 --- a/editor.planx.uk/src/@planx/components/List/schemas/ResidentialUnits/Proposed.ts +++ b/editor.planx.uk/src/@planx/components/List/schemas/ResidentialUnits/Proposed.ts @@ -10,8 +10,17 @@ export const ResidentialUnitsProposed: Schema = { fn: "development", options: [ { id: "newBuild", data: { text: "New build", val: "newBuild" } }, - { id: "changeOfUseFrom", data: { text: "Change of use of existing single home", val: "changeOfUseFrom" } }, - { id: "changeOfUseTo", data: { text: "Change of use to a home", val: "changeOfUseTo" } }, + { + id: "changeOfUseFrom", + data: { + text: "Change of use of existing single home", + val: "changeOfUseFrom", + }, + }, + { + id: "changeOfUseTo", + data: { text: "Change of use to a home", val: "changeOfUseTo" }, + }, ], }, }, diff --git a/editor.planx.uk/src/@planx/components/NumberInput/Public.test.tsx b/editor.planx.uk/src/@planx/components/NumberInput/Public.test.tsx index 016150cc80..9756edf100 100644 --- a/editor.planx.uk/src/@planx/components/NumberInput/Public.test.tsx +++ b/editor.planx.uk/src/@planx/components/NumberInput/Public.test.tsx @@ -39,12 +39,21 @@ test("requires a positive number to be input by default", async () => { const handleSubmit = jest.fn(); const { user } = setup( - , + , ); - expect(screen.getByRole("heading")).toHaveTextContent("How many doors are you adding?"); + expect(screen.getByRole("heading")).toHaveTextContent( + "How many doors are you adding?", + ); - await user.type(screen.getByLabelText("How many doors are you adding?"), "-1"); + await user.type( + screen.getByLabelText("How many doors are you adding?"), + "-1", + ); await user.click(screen.getByTestId("continue-button")); expect(screen.getByText("Enter a positive number")).toBeInTheDocument(); @@ -55,10 +64,17 @@ test("allows negative numbers to be input when toggled on by editor", async () = const handleSubmit = jest.fn(); const { user } = setup( - , + , ); - expect(screen.getByRole("heading")).toHaveTextContent("What's the temperature?"); + expect(screen.getByRole("heading")).toHaveTextContent( + "What's the temperature?", + ); await user.type(screen.getByLabelText("What's the temperature?"), "-10"); await user.click(screen.getByTestId("continue-button")); diff --git a/editor.planx.uk/src/@planx/components/SetValue/utils.test.ts b/editor.planx.uk/src/@planx/components/SetValue/utils.test.ts index c556459134..9fb490626d 100644 --- a/editor.planx.uk/src/@planx/components/SetValue/utils.test.ts +++ b/editor.planx.uk/src/@planx/components/SetValue/utils.test.ts @@ -1,7 +1,7 @@ -import { SetValue } from "./model"; -import { handleSetValue } from "./utils"; import { Store } from "pages/FlowEditor/lib/store"; +import { SetValue } from "./model"; +import { handleSetValue } from "./utils"; describe("calculateNewValues() helper function", () => { describe('"replace" operation', () => { @@ -12,26 +12,29 @@ describe("calculateNewValues() helper function", () => { { previous: ["lion"], expected: ["lion"] }, { previous: ["bear", "dog", "monkey"], expected: ["lion"] }, { previous: ["bear", "dog", "lion"], expected: ["lion"] }, - ])('input of $previous sets passport value to be $expected', ({ previous, expected }) => { - const mockKey = "myAnimals"; - const mockSetValue: SetValue = { - operation: "replace", - fn: mockKey, - val: "lion", - }; - const mockPassport: Store.passport = { - data: { mockNode: mockSetValue, [mockKey]: previous }, - }; + ])( + "input of $previous sets passport value to be $expected", + ({ previous, expected }) => { + const mockKey = "myAnimals"; + const mockSetValue: SetValue = { + operation: "replace", + fn: mockKey, + val: "lion", + }; + const mockPassport: Store.passport = { + data: { mockNode: mockSetValue, [mockKey]: previous }, + }; - const updatedPassport = handleSetValue({ - nodeData: mockSetValue, - previousValues: previous, - passport: mockPassport, - }); + const updatedPassport = handleSetValue({ + nodeData: mockSetValue, + previousValues: previous, + passport: mockPassport, + }); - const actual = updatedPassport.data?.[mockKey]; - expect(actual).toEqual(expected); - }); + const actual = updatedPassport.data?.[mockKey]; + expect(actual).toEqual(expected); + }, + ); }); describe('"append" operation', () => { @@ -45,26 +48,29 @@ describe("calculateNewValues() helper function", () => { expected: ["bear", "dog", "monkey", "lion"], }, { previous: ["bear", "dog", "lion"], expected: ["bear", "dog", "lion"] }, - ])('input of $previous sets passport value to be $expected', ({ previous, expected }) => { - const mockKey = "myAnimals"; - const mockSetValue: SetValue = { - operation: "append", - fn: mockKey, - val: "lion", - }; - const mockPassport: Store.passport = { - data: { mockNode: mockSetValue, [mockKey]: previous }, - }; + ])( + "input of $previous sets passport value to be $expected", + ({ previous, expected }) => { + const mockKey = "myAnimals"; + const mockSetValue: SetValue = { + operation: "append", + fn: mockKey, + val: "lion", + }; + const mockPassport: Store.passport = { + data: { mockNode: mockSetValue, [mockKey]: previous }, + }; - const updatedPassport = handleSetValue({ - nodeData: mockSetValue, - previousValues: previous, - passport: mockPassport, - }); + const updatedPassport = handleSetValue({ + nodeData: mockSetValue, + previousValues: previous, + passport: mockPassport, + }); - const actual = updatedPassport.data?.[mockKey]; - expect(actual).toEqual(expected); - }); + const actual = updatedPassport.data?.[mockKey]; + expect(actual).toEqual(expected); + }, + ); }); describe('"removeOne" operation', () => { @@ -78,26 +84,29 @@ describe("calculateNewValues() helper function", () => { expected: ["bear", "dog", "monkey"], }, { previous: ["bear", "dog", "lion"], expected: ["bear", "dog"] }, - ])('input of $previous sets passport value to be $expected', ({ previous, expected }) => { - const mockKey = "myAnimals"; - const mockSetValue: SetValue = { - operation: "removeOne", - fn: mockKey, - val: "lion", - }; - const mockPassport: Store.passport = { - data: { mockNode: mockSetValue, [mockKey]: previous }, - }; + ])( + "input of $previous sets passport value to be $expected", + ({ previous, expected }) => { + const mockKey = "myAnimals"; + const mockSetValue: SetValue = { + operation: "removeOne", + fn: mockKey, + val: "lion", + }; + const mockPassport: Store.passport = { + data: { mockNode: mockSetValue, [mockKey]: previous }, + }; - const updatedPassport = handleSetValue({ - nodeData: mockSetValue, - previousValues: previous, - passport: mockPassport, - }); + const updatedPassport = handleSetValue({ + nodeData: mockSetValue, + previousValues: previous, + passport: mockPassport, + }); - const actual = updatedPassport.data?.[mockKey]; - expect(actual).toEqual(expected); - }); + const actual = updatedPassport.data?.[mockKey]; + expect(actual).toEqual(expected); + }, + ); }); describe('"removeAll" operation', () => { @@ -111,25 +120,28 @@ describe("calculateNewValues() helper function", () => { expected: undefined, }, { previous: ["bear", "dog", "lion"], expected: undefined }, - ])('input of $previous sets passport value to be $expected', ({ previous, expected }) => { - const mockKey = "myAnimals"; - const mockSetValue: SetValue = { - operation: "removeAll", - fn: mockKey, - val: "lion", - }; - const mockPassport: Store.passport = { - data: { mockNode: mockSetValue, [mockKey]: previous }, - }; + ])( + "input of $previous sets passport value to be $expected", + ({ previous, expected }) => { + const mockKey = "myAnimals"; + const mockSetValue: SetValue = { + operation: "removeAll", + fn: mockKey, + val: "lion", + }; + const mockPassport: Store.passport = { + data: { mockNode: mockSetValue, [mockKey]: previous }, + }; - const updatedPassport = handleSetValue({ - nodeData: mockSetValue, - previousValues: previous, - passport: mockPassport, - }); + const updatedPassport = handleSetValue({ + nodeData: mockSetValue, + previousValues: previous, + passport: mockPassport, + }); - const actual = updatedPassport.data?.[mockKey]; - expect(actual).toEqual(expected); - }); + const actual = updatedPassport.data?.[mockKey]; + expect(actual).toEqual(expected); + }, + ); }); }); diff --git a/editor.planx.uk/src/@planx/components/SetValue/utils.ts b/editor.planx.uk/src/@planx/components/SetValue/utils.ts index be6756dff3..46df9c9431 100644 --- a/editor.planx.uk/src/@planx/components/SetValue/utils.ts +++ b/editor.planx.uk/src/@planx/components/SetValue/utils.ts @@ -1,4 +1,5 @@ import { Store } from "pages/FlowEditor/lib/store"; + import { SetValue } from "./model"; type PreviousValues = string | string[] | undefined; diff --git a/editor.planx.uk/src/@planx/components/shared/Buttons/Tag.tsx b/editor.planx.uk/src/@planx/components/shared/Buttons/Tag.tsx index effbd45a40..c1740b7783 100644 --- a/editor.planx.uk/src/@planx/components/shared/Buttons/Tag.tsx +++ b/editor.planx.uk/src/@planx/components/shared/Buttons/Tag.tsx @@ -67,7 +67,9 @@ export default function Tag(props: Props): FCReturn { id={id} tagType={tagType} > - The status of this section of the application is: + + The status of this section of the application is: + {children} ); diff --git a/editor.planx.uk/src/@planx/components/shared/Radio/BasicRadio.tsx b/editor.planx.uk/src/@planx/components/shared/Radio/BasicRadio.tsx index da3e7e2aa2..55a9a7606b 100644 --- a/editor.planx.uk/src/@planx/components/shared/Radio/BasicRadio.tsx +++ b/editor.planx.uk/src/@planx/components/shared/Radio/BasicRadio.tsx @@ -12,7 +12,12 @@ export interface Props { value?: string; } -const BasicRadio: React.FC = ({ id, onChange, title, variant = "default" }) => ( +const BasicRadio: React.FC = ({ + id, + onChange, + title, + variant = "default", +}) => ( { diff --git a/editor.planx.uk/src/components/Feedback/FeedbackForm.stories.tsx b/editor.planx.uk/src/components/Feedback/FeedbackForm.stories.tsx index 2d60bd235e..4582ce7235 100644 --- a/editor.planx.uk/src/components/Feedback/FeedbackForm.stories.tsx +++ b/editor.planx.uk/src/components/Feedback/FeedbackForm.stories.tsx @@ -59,4 +59,4 @@ export const MissingInputForm: Story = { const submitButton = canvas.getByRole("button", { name: "Send feedback" }); await userEvent.click(submitButton); }, -}; \ No newline at end of file +}; diff --git a/editor.planx.uk/src/lib/feedback.ts b/editor.planx.uk/src/lib/feedback.ts index f3b8b3f7a0..fec8766746 100644 --- a/editor.planx.uk/src/lib/feedback.ts +++ b/editor.planx.uk/src/lib/feedback.ts @@ -41,7 +41,7 @@ export async function getInternalFeedbackMetadata(): Promise { nodeType: node?.type ? TYPES[node.type] : null, device: Bowser.parse(window.navigator.userAgent), userData: userData, - nodeData: node?.data + nodeData: node?.data, }; return metadata; diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/ButtonForm.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/ButtonForm.tsx index e4f4adc681..bab3a5c61a 100644 --- a/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/ButtonForm.tsx +++ b/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/ButtonForm.tsx @@ -11,8 +11,8 @@ import InputDescription from "ui/editor/InputDescription"; import InputRow from "ui/shared/InputRow"; import InputRowItem from "ui/shared/InputRowItem"; -import { DesignPreview, FormProps } from "."; import { SettingsForm } from "../shared/SettingsForm"; +import { DesignPreview, FormProps } from "."; export const ButtonForm: React.FC = ({ formikConfig, diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/FaviconForm.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/FaviconForm.tsx index 3a7fa0f608..14a5386a88 100644 --- a/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/FaviconForm.tsx +++ b/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/FaviconForm.tsx @@ -10,8 +10,8 @@ import InputRow from "ui/shared/InputRow"; import InputRowItem from "ui/shared/InputRowItem"; import InputRowLabel from "ui/shared/InputRowLabel"; -import { FormProps } from "."; import { SettingsForm } from "../shared/SettingsForm"; +import { FormProps } from "."; export const FaviconForm: React.FC = ({ formikConfig, diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/TextLinkForm.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/TextLinkForm.tsx index 98c0fea0e6..4b341a74b4 100644 --- a/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/TextLinkForm.tsx +++ b/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/TextLinkForm.tsx @@ -9,8 +9,8 @@ import InputDescription from "ui/editor/InputDescription"; import InputRow from "ui/shared/InputRow"; import InputRowItem from "ui/shared/InputRowItem"; -import { DesignPreview, FormProps } from "."; import { SettingsForm } from "../shared/SettingsForm"; +import { DesignPreview, FormProps } from "."; export const TextLinkForm: React.FC = ({ formikConfig, diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/ThemeAndLogoForm.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/ThemeAndLogoForm.tsx index afc872074f..af5c9e8da4 100644 --- a/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/ThemeAndLogoForm.tsx +++ b/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/ThemeAndLogoForm.tsx @@ -12,8 +12,8 @@ import InputRow from "ui/shared/InputRow"; import InputRowItem from "ui/shared/InputRowItem"; import InputRowLabel from "ui/shared/InputRowLabel"; -import { DesignPreview, FormProps } from "."; import { SettingsForm } from "../shared/SettingsForm"; +import { DesignPreview, FormProps } from "."; export const ThemeAndLogoForm: React.FC = ({ formikConfig, diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Sidebar/EditHistory.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Sidebar/EditHistory.tsx index b174681364..c92b60b6b6 100644 --- a/editor.planx.uk/src/pages/FlowEditor/components/Sidebar/EditHistory.tsx +++ b/editor.planx.uk/src/pages/FlowEditor/components/Sidebar/EditHistory.tsx @@ -18,9 +18,9 @@ import { OT } from "@planx/graph/types"; import DelayedLoadingIndicator from "components/DelayedLoadingIndicator"; import React, { useState } from "react"; import { FONT_WEIGHT_SEMI_BOLD } from "theme"; +import { Operation } from "types"; import { useStore } from "../../lib/store"; -import { Operation } from "types"; import { formatLastEditDate } from "../../utils"; const TooltipWrap = styled(({ className, ...props }: TooltipProps) => ( diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Sidebar/PublishDialog.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Sidebar/PublishDialog.tsx index da3cd924cb..cb33d3ba3c 100644 --- a/editor.planx.uk/src/pages/FlowEditor/components/Sidebar/PublishDialog.tsx +++ b/editor.planx.uk/src/pages/FlowEditor/components/Sidebar/PublishDialog.tsx @@ -12,11 +12,11 @@ import ListItemIcon from "@mui/material/ListItemIcon"; import ListItemText from "@mui/material/ListItemText"; import Typography from "@mui/material/Typography"; import { ComponentType as TYPES } from "@opensystemslab/planx-core/types"; +import { formatLastPublishMessage } from "pages/FlowEditor/utils"; import React, { useState } from "react"; import { useAsync } from "react-use"; import Caret from "ui/icons/Caret"; -import { formatLastPublishMessage } from "pages/FlowEditor/utils"; import { useStore } from "../../lib/store"; export interface AlteredNode { @@ -68,7 +68,9 @@ const AlteredNestedFlowListItem = (props: Portal) => { const _nestedFlowLastPublishedRequest = useAsync(async () => { const user = await lastPublisher(flowId); - setNestedFlowLastPublishedTitle(formatLastPublishMessage(publishedAt, user)); + setNestedFlowLastPublishedTitle( + formatLastPublishMessage(publishedAt, user), + ); }); return ( @@ -148,17 +150,23 @@ export const AlteredNodesSummaryContent = (props: { {`Changes`} {changeSummary["title"] && ( - - {changeSummary["title"]} - + {changeSummary["title"]} )} {(changeSummary["updated"] > 0 || changeSummary["deleted"] > 0) && ( - + {`${changeSummary["updated"]} nodes have been updated or added`} - + {`${changeSummary["deleted"]} nodes have been deleted`} @@ -228,14 +236,14 @@ export interface ValidationCheck { } export const ValidationChecks = (props: { - validationChecks: ValidationCheck[] + validationChecks: ValidationCheck[]; }) => { const { validationChecks } = props; const Icon: Record = { - "Pass": , - "Fail": , - "Not applicable": + Pass: , + Fail: , + "Not applicable": , }; return ( @@ -250,8 +258,27 @@ export const ValidationChecks = (props: { {Icon[check.status]} {check.title}} - secondary={{check.message}} + primary={ + + {check.title} + + } + secondary={ + + {check.message} + + } /> ))} @@ -259,4 +286,4 @@ export const ValidationChecks = (props: { ); -} +}; diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Sidebar/index.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Sidebar/index.tsx index 346ce7146e..1b42dd65b0 100644 --- a/editor.planx.uk/src/pages/FlowEditor/components/Sidebar/index.tsx +++ b/editor.planx.uk/src/pages/FlowEditor/components/Sidebar/index.tsx @@ -13,21 +13,26 @@ import DialogActions from "@mui/material/DialogActions"; import DialogContent from "@mui/material/DialogContent"; import DialogTitle from "@mui/material/DialogTitle"; import Link from "@mui/material/Link"; +import { styled } from "@mui/material/styles"; import Tab, { tabClasses } from "@mui/material/Tab"; import Tabs from "@mui/material/Tabs"; import Tooltip from "@mui/material/Tooltip"; import Typography from "@mui/material/Typography"; -import { styled } from "@mui/material/styles"; import { AxiosError } from "axios"; +import { formatLastPublishMessage } from "pages/FlowEditor/utils"; import React, { useState } from "react"; import { useAsync } from "react-use"; import Input from "ui/shared/Input"; -import { formatLastPublishMessage } from "pages/FlowEditor/utils"; import Questions from "../../../Preview/Questions"; import { useStore } from "../../lib/store"; import EditHistory from "./EditHistory"; -import { AlteredNode, AlteredNodesSummaryContent, ValidationCheck, ValidationChecks } from "./PublishDialog"; +import { + AlteredNode, + AlteredNodesSummaryContent, + ValidationCheck, + ValidationChecks, +} from "./PublishDialog"; type SidebarTabs = "PreviewBrowser" | "History"; @@ -174,7 +179,9 @@ const Sidebar: React.FC<{ const [lastPublishedTitle, setLastPublishedTitle] = useState( "This flow is not published yet", ); - const [validationChecks, setValidationChecks] = useState([]); + const [validationChecks, setValidationChecks] = useState( + [], + ); const [alteredNodes, setAlteredNodes] = useState(); const [dialogOpen, setDialogOpen] = useState(false); const [summary, setSummary] = useState(); @@ -189,9 +196,7 @@ const Sidebar: React.FC<{ setLastPublishedTitle("Checking for changes..."); const alteredFlow = await validateAndDiffFlow(flowId); setAlteredNodes( - alteredFlow?.data.alteredNodes - ? alteredFlow.data.alteredNodes - : [], + alteredFlow?.data.alteredNodes ? alteredFlow.data.alteredNodes : [], ); setLastPublishedTitle( alteredFlow?.data.alteredNodes @@ -201,9 +206,7 @@ const Sidebar: React.FC<{ setValidationChecks(alteredFlow?.data?.validationChecks); setDialogOpen(true); } catch (error) { - setLastPublishedTitle( - "Error checking for changes to publish", - ); + setLastPublishedTitle("Error checking for changes to publish"); if (error instanceof AxiosError) { alert(error.response?.data?.error); @@ -219,10 +222,7 @@ const Sidebar: React.FC<{ try { setDialogOpen(false); setLastPublishedTitle("Publishing changes..."); - const { alteredNodes, message } = await publishFlow( - flowId, - summary, - ); + const { alteredNodes, message } = await publishFlow(flowId, summary); setLastPublishedTitle( alteredNodes ? `Successfully published changes to ${alteredNodes.length} nodes` @@ -379,13 +379,17 @@ const Sidebar: React.FC<{ {alteredNodes?.length ? ( <> {`Preview these content changes in-service before publishing `} - + {`here (opens in a new tab).`} @@ -413,7 +417,12 @@ const Sidebar: React.FC<{ color="primary" variant="contained" onClick={handlePublish} - disabled={!alteredNodes || alteredNodes.length === 0 || validationChecks.filter((v) => v.status === "Fail").length > 0} + disabled={ + !alteredNodes || + alteredNodes.length === 0 || + validationChecks.filter((v) => v.status === "Fail").length > + 0 + } > PUBLISH diff --git a/editor.planx.uk/src/pages/FlowEditor/lib/store/preview.ts b/editor.planx.uk/src/pages/FlowEditor/lib/store/preview.ts index 97f437e533..be77eeeb9b 100644 --- a/editor.planx.uk/src/pages/FlowEditor/lib/store/preview.ts +++ b/editor.planx.uk/src/pages/FlowEditor/lib/store/preview.ts @@ -11,6 +11,7 @@ import { import { ComponentType as TYPES } from "@opensystemslab/planx-core/types"; import { FileList } from "@planx/components/FileUploadAndLabel/model"; import { SetValue } from "@planx/components/SetValue/model"; +import { handleSetValue } from "@planx/components/SetValue/utils"; import { sortIdsDepthFirst } from "@planx/graph"; import { logger } from "airbrake"; import { objectWithoutNullishValues } from "lib/objectHelpers"; @@ -29,7 +30,6 @@ import { ApplicationPath } from "./../../../../types"; import type { Store } from "."; import { NavigationStore } from "./navigation"; import type { SharedStore } from "./shared"; -import { handleSetValue } from "@planx/components/SetValue/utils"; const SUPPORTED_DECISION_TYPES = [TYPES.Checklist, TYPES.Question]; let memoizedPreviousCardId: string | undefined = undefined; @@ -902,4 +902,4 @@ export const removeNodesDependentOnPassport = ( return acc; }, [] as string[]); return { removedNodeIds, breadcrumbsWithoutPassportData: newBreadcrumbs }; -}; \ No newline at end of file +}; diff --git a/editor.planx.uk/src/pages/FlowEditor/utils.ts b/editor.planx.uk/src/pages/FlowEditor/utils.ts index ed3c5d0688..a9f28b54ee 100644 --- a/editor.planx.uk/src/pages/FlowEditor/utils.ts +++ b/editor.planx.uk/src/pages/FlowEditor/utils.ts @@ -9,7 +9,7 @@ export const formatLastEditDate = (date: string): string => { export const formatLastEditMessage = ( date: string, - actor?: { firstName: string; lastName: string } + actor?: { firstName: string; lastName: string }, ): string => { if (!actor) { return `Last edited ${formatLastEditDate(date)}`; diff --git a/editor.planx.uk/src/pages/PlatformAdminPanel.tsx b/editor.planx.uk/src/pages/PlatformAdminPanel.tsx index 30fcd5456b..540fc44abe 100644 --- a/editor.planx.uk/src/pages/PlatformAdminPanel.tsx +++ b/editor.planx.uk/src/pages/PlatformAdminPanel.tsx @@ -58,7 +58,7 @@ const TeamData: React.FC = ({ data }) => { const a4Endpoint = `${process.env.REACT_APP_API_URL}/gis/${data.slug}/article4-schema`; const fetcher = (url: string) => fetch(url).then((r) => r.json()); const { data: a4Check, isValidating } = useSWR( - () => data.slug ? a4Endpoint : null, + () => (data.slug ? a4Endpoint : null), fetcher, ); @@ -123,7 +123,13 @@ const TeamData: React.FC = ({ data }) => { <> {"Article 4s (API)"} - {!isValidating && a4Check?.status ? : } + + {!isValidating && a4Check?.status ? ( + + ) : ( + + )} + <> {"Reference code"} diff --git a/editor.planx.uk/src/pages/layout/PublicLayout.tsx b/editor.planx.uk/src/pages/layout/PublicLayout.tsx index bb854c417e..e3c63cd5fb 100644 --- a/editor.planx.uk/src/pages/layout/PublicLayout.tsx +++ b/editor.planx.uk/src/pages/layout/PublicLayout.tsx @@ -52,10 +52,10 @@ const PublicFooter: React.FC = () => { const globalFooterItems = globalSettings?.footerContent ? Object.entries(globalSettings?.footerContent).map(([slug, item]) => ({ - title: item.heading, - content: item.content, - href: makeHref(slug), - })) + title: item.heading, + content: item.content, + href: makeHref(slug), + })) : []; const footerItems = [...flowSettingsContent, ...globalFooterItems].filter( diff --git a/editor.planx.uk/src/themeOverrides.d.ts b/editor.planx.uk/src/themeOverrides.d.ts index 39704669b5..37b3bbbea0 100644 --- a/editor.planx.uk/src/themeOverrides.d.ts +++ b/editor.planx.uk/src/themeOverrides.d.ts @@ -1,8 +1,8 @@ import "@mui/material/Chip"; // eslint-disable-next-line no-restricted-imports import "@mui/material/styles/createPalette"; -import { RadioProps } from "@mui/material/Radio"; +import { RadioProps } from "@mui/material/Radio"; declare module "@mui/material/Chip" { interface ChipPropsVariantOverrides { diff --git a/editor.planx.uk/src/ui/shared/Checkbox.tsx b/editor.planx.uk/src/ui/shared/Checkbox.tsx index 297c1115d9..c3ba044fb5 100644 --- a/editor.planx.uk/src/ui/shared/Checkbox.tsx +++ b/editor.planx.uk/src/ui/shared/Checkbox.tsx @@ -47,10 +47,15 @@ export interface Props { id?: string; checked: boolean; onChange?: (event?: React.MouseEvent) => void; - inputProps?: React.InputHTMLAttributes + inputProps?: React.InputHTMLAttributes; } -export default function Checkbox({ id, checked, onChange, inputProps }: Props): FCReturn { +export default function Checkbox({ + id, + checked, + onChange, + inputProps, +}: Props): FCReturn { return ( onChange && onChange()}> Date: Tue, 25 Jun 2024 11:23:34 +0100 Subject: [PATCH 6/7] chore: Remove ``has_planning_data`` from team_settings table (#3314) --- .../Settings/GeneralSettings/index.tsx | 6 ------ hasura.planx.uk/metadata/tables.yaml | 18 ++++++------------ .../down.sql | 4 ++++ .../up.sql | 1 + 4 files changed, 11 insertions(+), 18 deletions(-) create mode 100644 hasura.planx.uk/migrations/1719307850258_alter_table_public_team_settings_drop_column_has_planning_data/down.sql create mode 100644 hasura.planx.uk/migrations/1719307850258_alter_table_public_team_settings_drop_column_has_planning_data/up.sql diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Settings/GeneralSettings/index.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Settings/GeneralSettings/index.tsx index 7ce9edd670..4f4ee0195f 100644 --- a/editor.planx.uk/src/pages/FlowEditor/components/Settings/GeneralSettings/index.tsx +++ b/editor.planx.uk/src/pages/FlowEditor/components/Settings/GeneralSettings/index.tsx @@ -15,9 +15,6 @@ export interface GeneralSettings { helpPhone: string; helpOpeningHours: string; homepage: string; - isPlanningDataCollected: boolean; - portalName: string; - portalUrl: string; } export interface FormProps { @@ -36,9 +33,6 @@ const GeneralSettings: React.FC = () => { helpPhone: "", helpOpeningHours: "", homepage: "", - isPlanningDataCollected: true, - portalName: "", - portalUrl: "", }; useEffect(() => { diff --git a/hasura.planx.uk/metadata/tables.yaml b/hasura.planx.uk/metadata/tables.yaml index d96ec6375d..e920555a75 100644 --- a/hasura.planx.uk/metadata/tables.yaml +++ b/hasura.planx.uk/metadata/tables.yaml @@ -1660,9 +1660,6 @@ - role: api permission: columns: - - has_planning_data - - id - - team_id - boundary_json - boundary_url - email_reply_to_id @@ -1672,15 +1669,14 @@ - help_opening_hours - help_phone - homepage + - id - reference_code + - team_id filter: {} comment: "" - role: platformAdmin permission: columns: - - has_planning_data - - id - - team_id - boundary_json - boundary_url - email_reply_to_id @@ -1690,7 +1686,9 @@ - help_opening_hours - help_phone - homepage + - id - reference_code + - team_id filter: {} comment: "" - role: public @@ -1700,7 +1698,6 @@ - boundary_url - external_planning_site_name - external_planning_site_url - - has_planning_data - homepage - id - reference_code @@ -1710,9 +1707,6 @@ - role: teamEditor permission: columns: - - has_planning_data - - id - - team_id - boundary_json - boundary_url - email_reply_to_id @@ -1722,7 +1716,9 @@ - help_opening_hours - help_phone - homepage + - id - reference_code + - team_id filter: {} comment: "" update_permissions: @@ -1733,7 +1729,6 @@ - email_reply_to_id - external_planning_site_name - external_planning_site_url - - has_planning_data - help_email - help_opening_hours - help_phone @@ -1749,7 +1744,6 @@ - email_reply_to_id - external_planning_site_name - external_planning_site_url - - has_planning_data - help_email - help_opening_hours - help_phone diff --git a/hasura.planx.uk/migrations/1719307850258_alter_table_public_team_settings_drop_column_has_planning_data/down.sql b/hasura.planx.uk/migrations/1719307850258_alter_table_public_team_settings_drop_column_has_planning_data/down.sql new file mode 100644 index 0000000000..0fc61106ff --- /dev/null +++ b/hasura.planx.uk/migrations/1719307850258_alter_table_public_team_settings_drop_column_has_planning_data/down.sql @@ -0,0 +1,4 @@ +alter table "public"."team_settings" add column "has_planning_data" bool; +comment on column "public"."team_settings"."has_planning_data" is E'Global settings for boundary and contact details'; +alter table "public"."team_settings" alter column "has_planning_data" set default false; +alter table "public"."team_settings" alter column "has_planning_data" drop not null; diff --git a/hasura.planx.uk/migrations/1719307850258_alter_table_public_team_settings_drop_column_has_planning_data/up.sql b/hasura.planx.uk/migrations/1719307850258_alter_table_public_team_settings_drop_column_has_planning_data/up.sql new file mode 100644 index 0000000000..e24d901a31 --- /dev/null +++ b/hasura.planx.uk/migrations/1719307850258_alter_table_public_team_settings_drop_column_has_planning_data/up.sql @@ -0,0 +1 @@ +alter table "public"."team_settings" drop column "has_planning_data" cascade; From 579be6e2a3a084b0c2ffaa764ba6f4189cb4ec90 Mon Sep 17 00:00:00 2001 From: Rory Doak <138574807+RODO94@users.noreply.github.com> Date: Tue, 25 Jun 2024 14:17:49 +0100 Subject: [PATCH 7/7] refactor: [skip pizza] commenting out general settings tab (#3318) --- editor.planx.uk/src/routes/teamSettings.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/editor.planx.uk/src/routes/teamSettings.tsx b/editor.planx.uk/src/routes/teamSettings.tsx index 587160a6ea..c03254a546 100644 --- a/editor.planx.uk/src/routes/teamSettings.tsx +++ b/editor.planx.uk/src/routes/teamSettings.tsx @@ -47,11 +47,11 @@ const teamSettingsRoutes = compose( route: "design", Component: DesignSettings, }, - { + /* { name: "General", route: "general", Component: GeneralSettings, - }, + },*/ ]} /> ),