diff --git a/.env.example b/.env.example index 9ee369705a..5bb66c3715 100644 --- a/.env.example +++ b/.env.example @@ -51,6 +51,8 @@ SLACK_WEBHOOK_URL=👻 # Metabase analytics METABASE_PORT=5000 +METABASE_API_KEY=👻 +METABASE_URL_EXT=http://localhost:${METABASE_PORT} # Minio object storage server MINIO_PORT=9000 diff --git a/api.planx.uk/modules/saveAndReturn/service/validateSession.ts b/api.planx.uk/modules/saveAndReturn/service/validateSession.ts index 34445d26bc..5f197b7613 100644 --- a/api.planx.uk/modules/saveAndReturn/service/validateSession.ts +++ b/api.planx.uk/modules/saveAndReturn/service/validateSession.ts @@ -197,7 +197,12 @@ export async function findSession({ gql` query FindSession($sessionId: uuid!, $email: String!) { sessions: lowcal_sessions( - where: { id: { _eq: $sessionId }, email: { _eq: $email } } + where: { + id: { _eq: $sessionId } + email: { _eq: $email } + submitted_at: { _is_null: true } + deleted_at: { _is_null: true } + } limit: 1 ) { flow_id diff --git a/api.planx.uk/package.json b/api.planx.uk/package.json index 51aed359df..6e35d6fa51 100644 --- a/api.planx.uk/package.json +++ b/api.planx.uk/package.json @@ -11,7 +11,7 @@ }, "dependencies": { "@airbrake/node": "^2.1.8", - "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#e73d702", + "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#4cc216f", "@types/isomorphic-fetch": "^0.0.36", "adm-zip": "^0.5.10", "aws-sdk": "^2.1467.0", diff --git a/api.planx.uk/pnpm-lock.yaml b/api.planx.uk/pnpm-lock.yaml index 7803c5c01f..cdf19fd18c 100644 --- a/api.planx.uk/pnpm-lock.yaml +++ b/api.planx.uk/pnpm-lock.yaml @@ -14,8 +14,8 @@ dependencies: specifier: ^2.1.8 version: 2.1.8 '@opensystemslab/planx-core': - specifier: git+https://github.com/theopensystemslab/planx-core#e73d702 - version: github.com/theopensystemslab/planx-core/e73d702 + specifier: git+https://github.com/theopensystemslab/planx-core#4cc216f + version: github.com/theopensystemslab/planx-core/4cc216f '@types/isomorphic-fetch': specifier: ^0.0.36 version: 0.0.36 @@ -4379,8 +4379,8 @@ packages: semver: 7.6.3 dev: true - /marked@14.1.2: - resolution: {integrity: sha512-f3r0yqpz31VXiDB/wj9GaOB0a2PRLQl6vJmXiFrniNwjkKdvakqJRULhjFKJpxOchlCRiG5fcacoUZY5Xa6PEQ==} + /marked@14.1.3: + resolution: {integrity: sha512-ZibJqTULGlt9g5k4VMARAktMAjXoVnnr+Y3aCqW1oDftcV4BA3UmrBifzXoZyenHRk75csiPu9iwsTj4VNBT0g==} engines: {node: '>= 18'} hasBin: true dev: false @@ -6265,8 +6265,8 @@ packages: resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} dev: false - github.com/theopensystemslab/planx-core/e73d702: - resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/e73d702} + github.com/theopensystemslab/planx-core/4cc216f: + resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/4cc216f} name: '@opensystemslab/planx-core' version: 1.0.0 prepare: true @@ -6288,7 +6288,7 @@ packages: graphql-request: 6.1.0(graphql@16.9.0) json-schema-to-typescript: 15.0.2 lodash: 4.17.21 - marked: 14.1.2 + marked: 14.1.3 prettier: 3.3.3 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) diff --git a/e2e/tests/api-driven/package.json b/e2e/tests/api-driven/package.json index fc0011a301..8206c0d534 100644 --- a/e2e/tests/api-driven/package.json +++ b/e2e/tests/api-driven/package.json @@ -7,7 +7,7 @@ "packageManager": "pnpm@8.6.6", "dependencies": { "@cucumber/cucumber": "^9.3.0", - "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#e73d702", + "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#4cc216f", "axios": "^1.7.4", "dotenv": "^16.3.1", "dotenv-expand": "^10.0.0", diff --git a/e2e/tests/api-driven/pnpm-lock.yaml b/e2e/tests/api-driven/pnpm-lock.yaml index bd2bc19a5c..24e3695677 100644 --- a/e2e/tests/api-driven/pnpm-lock.yaml +++ b/e2e/tests/api-driven/pnpm-lock.yaml @@ -9,8 +9,8 @@ dependencies: specifier: ^9.3.0 version: 9.3.0 '@opensystemslab/planx-core': - specifier: git+https://github.com/theopensystemslab/planx-core#e73d702 - version: github.com/theopensystemslab/planx-core/e73d702 + specifier: git+https://github.com/theopensystemslab/planx-core#4cc216f + version: github.com/theopensystemslab/planx-core/4cc216f axios: specifier: ^1.7.4 version: 1.7.4 @@ -2040,8 +2040,8 @@ packages: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} dev: true - /marked@14.1.2: - resolution: {integrity: sha512-f3r0yqpz31VXiDB/wj9GaOB0a2PRLQl6vJmXiFrniNwjkKdvakqJRULhjFKJpxOchlCRiG5fcacoUZY5Xa6PEQ==} + /marked@14.1.3: + resolution: {integrity: sha512-ZibJqTULGlt9g5k4VMARAktMAjXoVnnr+Y3aCqW1oDftcV4BA3UmrBifzXoZyenHRk75csiPu9iwsTj4VNBT0g==} engines: {node: '>= 18'} hasBin: true dev: false @@ -2956,8 +2956,8 @@ packages: resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} dev: false - github.com/theopensystemslab/planx-core/e73d702: - resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/e73d702} + github.com/theopensystemslab/planx-core/4cc216f: + resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/4cc216f} name: '@opensystemslab/planx-core' version: 1.0.0 prepare: true @@ -2979,7 +2979,7 @@ packages: graphql-request: 6.1.0(graphql@16.9.0) json-schema-to-typescript: 15.0.2 lodash: 4.17.21 - marked: 14.1.2 + marked: 14.1.3 prettier: 3.3.3 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) diff --git a/e2e/tests/ui-driven/package.json b/e2e/tests/ui-driven/package.json index a677f5b43e..a59ab00436 100644 --- a/e2e/tests/ui-driven/package.json +++ b/e2e/tests/ui-driven/package.json @@ -8,7 +8,7 @@ "postinstall": "./install-dependencies.sh" }, "dependencies": { - "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#e73d702", + "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#4cc216f", "axios": "^1.7.4", "dotenv": "^16.3.1", "eslint": "^8.56.0", @@ -16,7 +16,7 @@ "graphql-request": "^6.1.0", "isomorphic-fetch": "^3.0.0", "jsonwebtoken": "^9.0.2", - "serve": "^14.2.1", + "serve": "^14.2.4", "uuid": "^9.0.1" }, "packageManager": "pnpm@8.6.6", diff --git a/e2e/tests/ui-driven/pnpm-lock.yaml b/e2e/tests/ui-driven/pnpm-lock.yaml index 70621a1f3b..f7f4cd6bff 100644 --- a/e2e/tests/ui-driven/pnpm-lock.yaml +++ b/e2e/tests/ui-driven/pnpm-lock.yaml @@ -6,8 +6,8 @@ settings: dependencies: '@opensystemslab/planx-core': - specifier: git+https://github.com/theopensystemslab/planx-core#e73d702 - version: github.com/theopensystemslab/planx-core/e73d702 + specifier: git+https://github.com/theopensystemslab/planx-core#4cc216f + version: github.com/theopensystemslab/planx-core/4cc216f axios: specifier: ^1.7.4 version: 1.7.4 @@ -30,8 +30,8 @@ dependencies: specifier: ^9.0.2 version: 9.0.2 serve: - specifier: ^14.2.1 - version: 14.2.1 + specifier: ^14.2.4 + version: 14.2.4 uuid: specifier: ^9.0.1 version: 9.0.1 @@ -698,8 +698,8 @@ packages: /@ungap/structured-clone@1.2.0: resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} - /@zeit/schemas@2.29.0: - resolution: {integrity: sha512-g5QiLIfbg3pLuYUJPlisNKY+epQJTcMDsOnVNkscrDP1oi7vmJnzOANYJI/1pZcVJ6umUkBv3aFtlg1UvUHGzA==} + /@zeit/schemas@2.36.0: + resolution: {integrity: sha512-7kjMwcChYEzMKjeex9ZFXkt1AyNov9R5HZtjBKVsmVpw7pa7ZtlCGvCBC2vnnXctaYN+aRI61HjIqeetZW5ROg==} dev: false /accepts@1.3.8: @@ -741,8 +741,8 @@ packages: json-schema-traverse: 0.4.1 uri-js: 4.4.1 - /ajv@8.11.0: - resolution: {integrity: sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==} + /ajv@8.12.0: + resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==} dependencies: fast-deep-equal: 3.1.3 json-schema-traverse: 1.0.0 @@ -1399,12 +1399,6 @@ packages: resolution: {integrity: sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==} dev: false - /fast-url-parser@1.1.3: - resolution: {integrity: sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==} - dependencies: - punycode: 1.4.1 - dev: false - /fast-xml-parser@4.5.0: resolution: {integrity: sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg==} hasBin: true @@ -1884,8 +1878,8 @@ packages: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} dev: false - /marked@14.1.2: - resolution: {integrity: sha512-f3r0yqpz31VXiDB/wj9GaOB0a2PRLQl6vJmXiFrniNwjkKdvakqJRULhjFKJpxOchlCRiG5fcacoUZY5Xa6PEQ==} + /marked@14.1.3: + resolution: {integrity: sha512-ZibJqTULGlt9g5k4VMARAktMAjXoVnnr+Y3aCqW1oDftcV4BA3UmrBifzXoZyenHRk75csiPu9iwsTj4VNBT0g==} engines: {node: '>= 18'} hasBin: true dev: false @@ -2132,8 +2126,8 @@ packages: minipass: 7.1.2 dev: false - /path-to-regexp@2.2.1: - resolution: {integrity: sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==} + /path-to-regexp@3.3.0: + resolution: {integrity: sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==} dev: false /path-type@4.0.0: @@ -2187,10 +2181,6 @@ packages: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} dev: false - /punycode@1.4.1: - resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} - dev: false - /punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -2358,26 +2348,25 @@ packages: hasBin: true dev: false - /serve-handler@6.1.5: - resolution: {integrity: sha512-ijPFle6Hwe8zfmBxJdE+5fta53fdIY0lHISJvuikXB3VYFafRjMRpOffSPvCYsbKyBA7pvy9oYr/BT1O3EArlg==} + /serve-handler@6.1.6: + resolution: {integrity: sha512-x5RL9Y2p5+Sh3D38Fh9i/iQ5ZK+e4xuXRd/pGbM4D13tgo/MGwbttUk8emytcr1YYzBYs+apnUngBDFYfpjPuQ==} dependencies: bytes: 3.0.0 content-disposition: 0.5.2 - fast-url-parser: 1.1.3 mime-types: 2.1.18 minimatch: 3.1.2 path-is-inside: 1.0.2 - path-to-regexp: 2.2.1 + path-to-regexp: 3.3.0 range-parser: 1.2.0 dev: false - /serve@14.2.1: - resolution: {integrity: sha512-48er5fzHh7GCShLnNyPBRPEjs2I6QBozeGr02gaacROiyS/8ARADlj595j39iZXAqBbJHH/ivJJyPRWY9sQWZA==} + /serve@14.2.4: + resolution: {integrity: sha512-qy1S34PJ/fcY8gjVGszDB3EXiPSk5FKhUa7tQe0UPRddxRidc2V6cNHPNewbE1D7MAkgLuWEt3Vw56vYy73tzQ==} engines: {node: '>= 14'} hasBin: true dependencies: - '@zeit/schemas': 2.29.0 - ajv: 8.11.0 + '@zeit/schemas': 2.36.0 + ajv: 8.12.0 arg: 5.0.2 boxen: 7.0.0 chalk: 5.0.1 @@ -2385,7 +2374,7 @@ packages: clipboardy: 3.0.0 compression: 1.7.4 is-port-reachable: 4.0.0 - serve-handler: 6.1.5 + serve-handler: 6.1.6 update-check: 1.5.4 transitivePeerDependencies: - supports-color @@ -2706,8 +2695,8 @@ packages: resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} dev: false - github.com/theopensystemslab/planx-core/e73d702: - resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/e73d702} + github.com/theopensystemslab/planx-core/4cc216f: + resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/4cc216f} name: '@opensystemslab/planx-core' version: 1.0.0 prepare: true @@ -2729,7 +2718,7 @@ packages: graphql-request: 6.1.0(graphql@16.9.0) json-schema-to-typescript: 15.0.2 lodash: 4.17.21 - marked: 14.1.2 + marked: 14.1.3 prettier: 3.3.3 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) diff --git a/editor.planx.uk/package.json b/editor.planx.uk/package.json index d27ebfcc2e..f938daf5c0 100644 --- a/editor.planx.uk/package.json +++ b/editor.planx.uk/package.json @@ -15,7 +15,7 @@ "@mui/material": "^5.15.10", "@mui/utils": "^5.15.11", "@opensystemslab/map": "1.0.0-alpha.3", - "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#e73d702", + "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#4cc216f", "@tiptap/core": "^2.4.0", "@tiptap/extension-bold": "^2.0.3", "@tiptap/extension-bubble-menu": "^2.1.13", diff --git a/editor.planx.uk/pnpm-lock.yaml b/editor.planx.uk/pnpm-lock.yaml index 8aefbd28fd..aab00a8439 100644 --- a/editor.planx.uk/pnpm-lock.yaml +++ b/editor.planx.uk/pnpm-lock.yaml @@ -47,8 +47,8 @@ dependencies: specifier: 1.0.0-alpha.3 version: 1.0.0-alpha.3 '@opensystemslab/planx-core': - specifier: git+https://github.com/theopensystemslab/planx-core#e73d702 - version: github.com/theopensystemslab/planx-core/e73d702(@types/react@18.2.45) + specifier: git+https://github.com/theopensystemslab/planx-core#4cc216f + version: github.com/theopensystemslab/planx-core/4cc216f(@types/react@18.2.45) '@tiptap/core': specifier: ^2.4.0 version: 2.4.0(@tiptap/pm@2.0.3) @@ -11050,8 +11050,8 @@ packages: react: 18.2.0 dev: true - /marked@14.1.2: - resolution: {integrity: sha512-f3r0yqpz31VXiDB/wj9GaOB0a2PRLQl6vJmXiFrniNwjkKdvakqJRULhjFKJpxOchlCRiG5fcacoUZY5Xa6PEQ==} + /marked@14.1.3: + resolution: {integrity: sha512-ZibJqTULGlt9g5k4VMARAktMAjXoVnnr+Y3aCqW1oDftcV4BA3UmrBifzXoZyenHRk75csiPu9iwsTj4VNBT0g==} engines: {node: '>= 18'} hasBin: true dev: false @@ -15359,9 +15359,9 @@ packages: use-sync-external-store: 1.2.0(react@18.2.0) dev: false - github.com/theopensystemslab/planx-core/e73d702(@types/react@18.2.45): - resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/e73d702} - id: github.com/theopensystemslab/planx-core/e73d702 + github.com/theopensystemslab/planx-core/4cc216f(@types/react@18.2.45): + resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/4cc216f} + id: github.com/theopensystemslab/planx-core/4cc216f name: '@opensystemslab/planx-core' version: 1.0.0 prepare: true @@ -15383,7 +15383,7 @@ packages: graphql-request: 6.1.0(graphql@16.9.0) json-schema-to-typescript: 15.0.2 lodash: 4.17.21 - marked: 14.1.2 + marked: 14.1.3 prettier: 3.3.3 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) diff --git a/editor.planx.uk/src/@planx/components/Calculate/Editor.tsx b/editor.planx.uk/src/@planx/components/Calculate/Editor.tsx index c1c0239ab5..ef45074716 100644 --- a/editor.planx.uk/src/@planx/components/Calculate/Editor.tsx +++ b/editor.planx.uk/src/@planx/components/Calculate/Editor.tsx @@ -3,11 +3,8 @@ import { styled } from "@mui/material/styles"; import Switch from "@mui/material/Switch"; import Typography from "@mui/material/Typography"; import { ComponentType as TYPES } from "@opensystemslab/planx-core/types"; -import { - EditorProps, - ICONS, -} from "@planx/components/ui"; -import { useFormik } from "formik"; +import { EditorProps, ICONS } from "@planx/components/ui"; +import { FormikErrors, useFormik } from "formik"; import React from "react"; import InputGroup from "ui/editor/InputGroup"; import { ModalFooter } from "ui/editor/ModalFooter"; @@ -36,12 +33,51 @@ export default function Component(props: Props) { props.handleSubmit({ type: TYPES.Calculate, data: newValues }); } }, - validate: () => { - // can parse formula - getVariables(formik.values.formula); + validate: (values) => { + const errors: FormikErrors = {}; + try { + // can parse formula + getVariables(formik.values.formula); + + // Validate formula + const result = evaluate( + values.formula, + values.samples, + values.defaults, + ); + + if (Number.isNaN(Number(result))) { + errors.formula = "Enter a formula which outputs a number"; + } + } catch (error: any) { + errors.formula = error.message; + } + return errors; }, + + validateOnChange: false, }); + const sampleResult = React.useMemo(() => { + try { + const result = evaluate( + formik.values.formula, + formik.values.samples, + formik.values.defaults, + ); + // Type guard as mathjs evaluates `m` to a "Unit" object for "meter" + if (!Number.isNaN(Number(result))) { + return result; + } else if (result === undefined) { + return "a number returned from the formula above"; + } else { + return `'${result}' which is of the type: ${typeof result}`; + } + } catch (e) { + return UNKNOWN; + } + }, [formik.values.formula, formik.values.defaults, formik.values.samples]); + /** * When the formula is updated, remove any defaults which are no longer used */ @@ -65,24 +101,6 @@ export default function Component(props: Props) { } }, [formik.values.formula]); - const sampleResult = React.useMemo(() => { - try { - const result = evaluate( - formik.values.formula, - formik.values.samples, - formik.values.defaults, - ); - // Type guard as mathjs evaluates `m` to a "Unit" object for "meter" - if (typeof result === "number") { - return result; - } else { - return UNKNOWN; - } - } catch (e) { - return UNKNOWN; - } - }, [formik.values.formula, formik.values.defaults, formik.values.samples]); - return (