From eef810d364e0da682647b2be18979610d4daabaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Thu, 10 Oct 2024 11:43:13 +0100 Subject: [PATCH 1/6] chore: Rename add-data to sync-data (#3795) --- README.md | 4 ++-- package.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 652f2bf647..65b5832e4e 100644 --- a/README.md +++ b/README.md @@ -79,14 +79,14 @@ We'd love to hear what you're building on Planx, don't hesitate to get in touch The root of the project has several scripts set up to help you manage your docker containers: -- `pnpm run up` alias for `pnpm recreate && pnpm add-data` +- `pnpm run up` alias for `pnpm recreate && pnpm sync-data` - `pnpm run down` alias for `pnpm destroy` - `pnpm run restart` alias for `pnpm stop && pnpm start` - `pnpm start` will (re)create docker containers without rebuilding them - `pnpm stop` will stop your docker containers without destroying them - `pnpm recreate` will build and (re)start your docker containers from scratch. - `pnpm destroy` will remove volumes (i.e. database data) and can be a useful hard reset when necessary. -- `pnpm add-data` will sync production records with modified data in your database +- `pnpm sync-data` will sync production records with modified data in your database - `pnpm clean-data` will sync production records and reset any modified data - `pnpm tests` will recreate your docker containers and include test services - `pnpm analytics` will recreate your docker containers and include [Metabase](https://www.metabase.com/) diff --git a/package.json b/package.json index 6fce77c2de..4309ce6171 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,13 @@ { "scripts": { - "up": "pnpm recreate && pnpm add-data", + "up": "pnpm recreate && pnpm sync-data", "down": "pnpm destroy", "restart": "pnpm stop && pnpm start", "start": "docker compose -f ./docker-compose.yml -f ./docker-compose.local.yml --profile mock-services up -d --quiet-pull", "stop": "docker compose -f ./docker-compose.yml -f ./docker-compose.local.yml --profile mock-services down", "recreate": "docker compose -f ./docker-compose.yml -f ./docker-compose.local.yml --profile mock-services up -d --quiet-pull --build --renew-anon-volumes --force-recreate", "destroy": "docker compose -f ./docker-compose.yml -f ./docker-compose.local.yml --profile mock-services down --remove-orphans -v", - "add-data": "docker compose -f ./docker-compose.yml -f ./docker-compose.local.yml -f ./docker-compose.seed.yml run seed-database", + "sync-data": "docker compose -f ./docker-compose.yml -f ./docker-compose.local.yml -f ./docker-compose.seed.yml run seed-database", "test-sync": "docker compose -f ./docker-compose.yml -f ./docker-compose.local.yml -f ./docker-compose.seed.yml run seed-database reset_flows", "clean-data": "docker compose -f ./docker-compose.yml -f ./docker-compose.seed.yml run seed-database reset_all", "tests": "./scripts/start-containers-for-tests.sh", From ff74183ca1c01abb623ee7a84c395c93a8255578 Mon Sep 17 00:00:00 2001 From: Ian Jones <51156018+ianjon3s@users.noreply.github.com> Date: Thu, 10 Oct 2024 11:53:39 +0100 Subject: [PATCH 2/6] fix: Remove redundant tooltip on URL copy (#3794) From 39ae01b223e02365d629ef8b7b6f6ec28025b02a Mon Sep 17 00:00:00 2001 From: Dan G Date: Thu, 10 Oct 2024 21:02:09 +0100 Subject: [PATCH 3/6] [api] remove `/logout` route (do not implement front channel logout) (#3759) --- api.planx.uk/modules/auth/controller.ts | 8 -------- api.planx.uk/modules/auth/docs.yaml | 23 ++++++++++++++++------- api.planx.uk/modules/auth/routes.ts | 2 -- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/api.planx.uk/modules/auth/controller.ts b/api.planx.uk/modules/auth/controller.ts index b1b5394193..a03fdc4b17 100644 --- a/api.planx.uk/modules/auth/controller.ts +++ b/api.planx.uk/modules/auth/controller.ts @@ -7,14 +7,6 @@ export const failedLogin: RequestHandler = (_req, _res, next) => message: "User failed to authenticate", }); -export const logout: RequestHandler = (req, res) => { - // TODO: implement dual purpose as Microsoft frontend logout channel - req.logout(() => { - // do nothing - }); - res.redirect(process.env.EDITOR_URL_EXT!); -}; - export const handleSuccess = (req: Request, res: Response) => { if (!req.user) { return res.json({ diff --git a/api.planx.uk/modules/auth/docs.yaml b/api.planx.uk/modules/auth/docs.yaml index 673e45e531..6377d20268 100644 --- a/api.planx.uk/modules/auth/docs.yaml +++ b/api.planx.uk/modules/auth/docs.yaml @@ -6,13 +6,6 @@ tags: - name: auth description: Authentication related requests paths: - /logout: - get: - summary: Logout from the PlanX service - tags: ["auth"] - responses: - "302": - description: Redirect to PlanX Editor /auth/login/failed: get: summary: Failed login @@ -44,3 +37,19 @@ paths: responses: "200": description: OK + /auth/microsoft: + get: + summary: Authenticate via Microsoft SSO + description: The first step in Microsoft authentication will involve redirecting the user to login.microsoftonline.com + tags: ["auth"] + responses: + "200": + description: OK + /auth/microsoft/callback: + get: + summary: Generate a JWT for an authenticated user + description: After authentication, Microsoft will redirect the user back to this route which generates a JWT for the user + tags: ["auth"] + responses: + "200": + description: OK diff --git a/api.planx.uk/modules/auth/routes.ts b/api.planx.uk/modules/auth/routes.ts index d349f55d94..1cf8d7771c 100644 --- a/api.planx.uk/modules/auth/routes.ts +++ b/api.planx.uk/modules/auth/routes.ts @@ -6,8 +6,6 @@ import * as Controller from "./controller.js"; export default (passport: Authenticator): Router => { const router = Router(); - router.get("/logout", Controller.logout); - // router.get("/auth/frontchannel-logout", Controller.frontChannelLogout) router.get("/auth/login/failed", Controller.failedLogin); router.get("/auth/google", Middleware.getGoogleAuthHandler(passport)); router.get( From 7477ac9a59a5d478279a1e549eafc321511cb329 Mon Sep 17 00:00:00 2001 From: Ian Jones <51156018+ianjon3s@users.noreply.github.com> Date: Fri, 11 Oct 2024 11:08:10 +0100 Subject: [PATCH 4/6] refactor: Result component multiline inputs (#3796) --- .../src/@planx/components/Result/Editor.tsx | 42 +++++++------------ 1 file changed, 15 insertions(+), 27 deletions(-) diff --git a/editor.planx.uk/src/@planx/components/Result/Editor.tsx b/editor.planx.uk/src/@planx/components/Result/Editor.tsx index 2caa01f7d0..b6a88e4471 100644 --- a/editor.planx.uk/src/@planx/components/Result/Editor.tsx +++ b/editor.planx.uk/src/@planx/components/Result/Editor.tsx @@ -1,13 +1,13 @@ import Box from "@mui/material/Box"; -import Collapse from "@mui/material/Collapse"; import Typography from "@mui/material/Typography"; import { Flag, flatFlags } from "@opensystemslab/planx-core/types"; import { ComponentType as TYPES } from "@opensystemslab/planx-core/types"; import { useFormik } from "formik"; import groupBy from "lodash/groupBy"; -import React, { useState } from "react"; +import React from "react"; import ModalSection from "ui/editor/ModalSection"; import ModalSectionContent from "ui/editor/ModalSectionContent"; +import InputLabel from "ui/public/InputLabel"; import Input from "ui/shared/Input"; import InputRow from "ui/shared/InputRow"; @@ -28,37 +28,25 @@ const FlagEditor: React.FC<{ }> = (props) => { const { flag, existingOverrides } = props; - const [expanded, setExpanded] = useState(false); - - const showEditedIndicator = Boolean(existingOverrides); - return ( - - setExpanded((x) => !x)}> - - {flag.text} - {showEditedIndicator && "*"} - + + + {flag.text} - - + + + props.onChange({ ...existingOverrides, heading: ev.target.value }) } /> - + + props.onChange({ ...existingOverrides, @@ -66,8 +54,8 @@ const FlagEditor: React.FC<{ }) } /> - - + + ); }; @@ -85,7 +73,7 @@ const ResultComponent: React.FC = (props) => { props.handleSubmit({ type: TYPES.Result, data: newValues }); } }, - validate: () => { }, + validate: () => {}, }); const allFlagsForSet = flags[formik.values.flagSet]; @@ -149,4 +137,4 @@ const ResultComponent: React.FC = (props) => { ); }; -export default ResultComponent; \ No newline at end of file +export default ResultComponent; From ca78e839cf7bab9eb1ef02e11bcaf572d5c16f24 Mon Sep 17 00:00:00 2001 From: Jessica McInchak Date: Fri, 11 Oct 2024 12:46:49 +0200 Subject: [PATCH 5/6] feat: Filters support choosing any flagset category (#3797) --- .../src/create-flow-with-geospatial.spec.ts | 2 +- .../@planx/components/Filter/Editor.test.tsx | 141 ++++++++++++++++++ .../src/@planx/components/Filter/Editor.tsx | 90 +++++++---- .../src/@planx/components/Filter/model.ts | 5 - .../components/Flow/components/Node.tsx | 12 +- 5 files changed, 213 insertions(+), 37 deletions(-) create mode 100644 editor.planx.uk/src/@planx/components/Filter/Editor.test.tsx delete mode 100644 editor.planx.uk/src/@planx/components/Filter/model.ts diff --git a/e2e/tests/ui-driven/src/create-flow-with-geospatial.spec.ts b/e2e/tests/ui-driven/src/create-flow-with-geospatial.spec.ts index 691f83a257..991dcd0bc5 100644 --- a/e2e/tests/ui-driven/src/create-flow-with-geospatial.spec.ts +++ b/e2e/tests/ui-driven/src/create-flow-with-geospatial.spec.ts @@ -62,7 +62,7 @@ test.describe("Flow creation, publish and preview", () => { await expect(editor.nodeList).toContainText([ "Find property", "an internal portalEdit Portal", - "(Flags Filter)ImmuneMissing informationPermission neededPrior approvalNoticePermitted developmentNot development(No Result)", + "Filter - Planning permissionImmuneMissing informationPermission neededPrior approvalNoticePermitted developmentNot developmentNo flag result", "Upload and label", "Confirm your location plan", "Planning constraints", diff --git a/editor.planx.uk/src/@planx/components/Filter/Editor.test.tsx b/editor.planx.uk/src/@planx/components/Filter/Editor.test.tsx new file mode 100644 index 0000000000..950d00b272 --- /dev/null +++ b/editor.planx.uk/src/@planx/components/Filter/Editor.test.tsx @@ -0,0 +1,141 @@ +import { + ComponentType as TYPES, + DEFAULT_FLAG_CATEGORY, + flatFlags, +} from "@opensystemslab/planx-core/types"; +import { fireEvent, screen, waitFor } from "@testing-library/react"; +import React from "react"; +import { setup } from "testUtils"; +import { vi } from "vitest"; + +import Filter from "./Editor"; + +test("Adding a filter without explicit props uses the default flagset", async () => { + const handleSubmit = vi.fn(); + + setup(); + + expect(screen.getByTestId("flagset-category-select")).toHaveValue( + DEFAULT_FLAG_CATEGORY, + ); + + fireEvent.submit(screen.getByTestId("filter-component-form")); + + await waitFor(() => + expect(handleSubmit).toHaveBeenCalledWith( + { + type: TYPES.Filter, + data: { + fn: "flag", + category: DEFAULT_FLAG_CATEGORY, + }, + }, + mockDefaultFlagOptions, + ), + ); +}); + +test("Adding a filter and selecting a flagset category", async () => { + const handleSubmit = vi.fn(); + + setup(); + + expect(screen.getByTestId("flagset-category-select")).toHaveValue( + DEFAULT_FLAG_CATEGORY, + ); + + fireEvent.change(screen.getByTestId("flagset-category-select"), { + target: { value: "Community infrastructure levy" }, + }); + + fireEvent.submit(screen.getByTestId("filter-component-form")); + + await waitFor(() => + expect(handleSubmit).toHaveBeenCalledWith( + { + type: TYPES.Filter, + data: { + fn: "flag", + category: "Community infrastructure levy", + }, + }, + mockCILFlagOptions, + ), + ); +}); + +test("Updating an existing filter to another category", async () => { + const handleSubmit = vi.fn(); + + setup( + , + ); + + expect(screen.getByTestId("flagset-category-select")).toHaveValue( + "Listed building consent", + ); + + fireEvent.change(screen.getByTestId("flagset-category-select"), { + target: { value: "Community infrastructure levy" }, + }); + + fireEvent.submit(screen.getByTestId("filter-component-form")); + + await waitFor(() => + expect(handleSubmit).toHaveBeenCalledWith( + { + type: TYPES.Filter, + data: { + fn: "flag", + category: "Community infrastructure levy", + }, + }, + mockCILFlagOptions, + ), + ); +}); + +const mockExistingFilterNode = { + data: { + fn: "flag", + category: "Listed building consent", + }, + type: 500, + edges: ["flag1", "flag2", "flag3", "flag4", "blankFlag"], +}; + +const mockDefaultFlagOptions = [ + ...flatFlags.filter((flag) => flag.category === DEFAULT_FLAG_CATEGORY), + { + category: DEFAULT_FLAG_CATEGORY, + text: "No flag result", + value: "", + }, +].map((flag) => ({ + type: TYPES.Answer, + data: { + text: flag.text, + val: flag.value, + }, +})); + +const mockCILFlagOptions = [ + ...flatFlags.filter( + (flag) => flag.category === "Community infrastructure levy", + ), + { + category: "Community infrastructure levy", + text: "No flag result", + value: "", + }, +].map((flag) => ({ + type: TYPES.Answer, + data: { + text: flag.text, + val: flag.value, + }, +})); diff --git a/editor.planx.uk/src/@planx/components/Filter/Editor.tsx b/editor.planx.uk/src/@planx/components/Filter/Editor.tsx index 5d2f3f2b03..b7b84f8ec3 100644 --- a/editor.planx.uk/src/@planx/components/Filter/Editor.tsx +++ b/editor.planx.uk/src/@planx/components/Filter/Editor.tsx @@ -1,52 +1,84 @@ +import Typography from "@mui/material/Typography"; import { + ComponentType as TYPES, DEFAULT_FLAG_CATEGORY, flatFlags, } from "@opensystemslab/planx-core/types"; -import { ComponentType as TYPES } from "@opensystemslab/planx-core/types"; import { useFormik } from "formik"; import React from "react"; +import ModalSection from "ui/editor/ModalSection"; +import ModalSectionContent from "ui/editor/ModalSectionContent"; + +import { ICONS } from "../ui"; export interface Props { id?: string; - handleSubmit?: (d: any, c?: any) => void; + handleSubmit?: (data: any, children?: any) => void; node?: any; } const Filter: React.FC = (props) => { const formik = useFormik({ - initialValues: {}, + initialValues: { + fn: "flag", + category: props?.node?.data?.category || DEFAULT_FLAG_CATEGORY, + }, onSubmit: (newValues) => { - if (props.handleSubmit) { - const children = props.id - ? undefined - : [ - ...flatFlags, - { - category: DEFAULT_FLAG_CATEGORY, - text: "(No Result)", - value: "", - }, - ] - .filter((f) => f.category === DEFAULT_FLAG_CATEGORY) - .map((f) => ({ - type: TYPES.Answer, - data: { - text: f.text, - val: f.value, - }, - })); + if (props?.handleSubmit) { + const children = [ + ...flatFlags, + { + category: formik.values.category, + text: "No flag result", + value: "", + }, + ] + .filter((f) => f.category === formik.values.category) + .map((f) => ({ + type: TYPES.Answer, + data: { + text: f.text, + val: f.value, + }, + })); - props.handleSubmit( - { type: TYPES.Filter, data: { newValues, fn: "flag" } }, - children, - ); + props.handleSubmit({ type: TYPES.Filter, data: newValues }, children); } }, - validate: () => {}, }); + + const categories = new Set(flatFlags.map((flag) => flag.category)); + return ( - ); }; diff --git a/editor.planx.uk/src/@planx/components/Filter/model.ts b/editor.planx.uk/src/@planx/components/Filter/model.ts deleted file mode 100644 index 0ca9cf9275..0000000000 --- a/editor.planx.uk/src/@planx/components/Filter/model.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface Filter { - id?: string; - handleSubmit?: (d: any, children: any) => void; - node?: any; -} diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Flow/components/Node.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Flow/components/Node.tsx index 2537272cb3..4a043a42a7 100644 --- a/editor.planx.uk/src/pages/FlowEditor/components/Flow/components/Node.tsx +++ b/editor.planx.uk/src/pages/FlowEditor/components/Flow/components/Node.tsx @@ -1,4 +1,7 @@ -import { ComponentType as TYPES } from "@opensystemslab/planx-core/types"; +import { + ComponentType as TYPES, + DEFAULT_FLAG_CATEGORY, +} from "@opensystemslab/planx-core/types"; import React from "react"; import { ErrorBoundary } from "react-error-boundary"; import { exhaustiveCheck } from "utils"; @@ -63,7 +66,12 @@ const Node: React.FC = (props) => { /> ); case TYPES.Filter: - return ; + return ( + + ); case TYPES.FindProperty: return ; case TYPES.List: From e0cc7429eb4c4923001efbb2226950589cc0ffdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Fri, 11 Oct 2024 13:43:15 +0100 Subject: [PATCH 6/6] fix: Visited node CSS (#3801) --- editor.planx.uk/src/pages/FlowEditor/floweditor.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/editor.planx.uk/src/pages/FlowEditor/floweditor.scss b/editor.planx.uk/src/pages/FlowEditor/floweditor.scss index e610e515d9..5998350e7b 100644 --- a/editor.planx.uk/src/pages/FlowEditor/floweditor.scss +++ b/editor.planx.uk/src/pages/FlowEditor/floweditor.scss @@ -110,7 +110,8 @@ $fontMonospace: "Source Code Pro", monospace; } } - &.wasVisited a { + &.wasVisited > div, + &.wasVisited > a { span { opacity: 1; }