diff --git a/api.planx.uk/package.json b/api.planx.uk/package.json index 8dc365fc3f..a06f6d54f4 100644 --- a/api.planx.uk/package.json +++ b/api.planx.uk/package.json @@ -4,7 +4,7 @@ "private": true, "dependencies": { "@airbrake/node": "^2.1.8", - "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#af52a96", + "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#b8da594", "@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 7a911153fd..1da044d430 100644 --- a/api.planx.uk/pnpm-lock.yaml +++ b/api.planx.uk/pnpm-lock.yaml @@ -12,8 +12,8 @@ dependencies: specifier: ^2.1.8 version: 2.1.8 '@opensystemslab/planx-core': - specifier: git+https://github.com/theopensystemslab/planx-core#af52a96 - version: github.com/theopensystemslab/planx-core/af52a96 + specifier: git+https://github.com/theopensystemslab/planx-core#b8da594 + version: github.com/theopensystemslab/planx-core/b8da594 '@types/isomorphic-fetch': specifier: ^0.0.36 version: 0.0.36 @@ -5648,6 +5648,7 @@ packages: chalk: 3.0.0 diff-match-patch: 1.0.5 dev: false + bundledDependencies: [] /jsonwebtoken@9.0.2: resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==} @@ -8311,8 +8312,8 @@ packages: resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} dev: false - github.com/theopensystemslab/planx-core/af52a96: - resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/af52a96} + github.com/theopensystemslab/planx-core/b8da594: + resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/b8da594} name: '@opensystemslab/planx-core' version: 1.0.0 prepare: true diff --git a/e2e/tests/api-driven/package.json b/e2e/tests/api-driven/package.json index ea437201a4..fe1a9961e5 100644 --- a/e2e/tests/api-driven/package.json +++ b/e2e/tests/api-driven/package.json @@ -6,8 +6,8 @@ }, "dependencies": { "@cucumber/cucumber": "^9.3.0", - "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#af52a96", - "axios": "^1.6.5", + "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#b8da594", + "axios": "^1.6.0", "dotenv": "^16.3.1", "dotenv-expand": "^10.0.0", "graphql": "^16.8.1", diff --git a/e2e/tests/api-driven/pnpm-lock.yaml b/e2e/tests/api-driven/pnpm-lock.yaml index 6ce9250672..261d850ac3 100644 --- a/e2e/tests/api-driven/pnpm-lock.yaml +++ b/e2e/tests/api-driven/pnpm-lock.yaml @@ -9,10 +9,10 @@ dependencies: specifier: ^9.3.0 version: 9.3.0 '@opensystemslab/planx-core': - specifier: git+https://github.com/theopensystemslab/planx-core#af52a96 - version: github.com/theopensystemslab/planx-core/af52a96 + specifier: git+https://github.com/theopensystemslab/planx-core#b8da594 + version: github.com/theopensystemslab/planx-core/b8da594 axios: - specifier: ^1.6.5 + specifier: ^1.6.0 version: 1.6.5 dotenv: specifier: ^16.3.1 @@ -2821,8 +2821,8 @@ packages: resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} dev: false - github.com/theopensystemslab/planx-core/af52a96: - resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/af52a96} + github.com/theopensystemslab/planx-core/b8da594: + resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/b8da594} name: '@opensystemslab/planx-core' version: 1.0.0 prepare: true diff --git a/e2e/tests/ui-driven/package.json b/e2e/tests/ui-driven/package.json index 3fd68035bf..ea97ee2264 100644 --- a/e2e/tests/ui-driven/package.json +++ b/e2e/tests/ui-driven/package.json @@ -8,8 +8,8 @@ "postinstall": "./install-dependencies.sh" }, "dependencies": { - "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#af52a96", - "axios": "^1.6.5", + "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#b8da594", + "axios": "^1.6.2", "dotenv": "^16.3.1", "eslint": "^8.56.0", "graphql": "^16.8.1", diff --git a/e2e/tests/ui-driven/pnpm-lock.yaml b/e2e/tests/ui-driven/pnpm-lock.yaml index 47e3143e3d..8119a798e9 100644 --- a/e2e/tests/ui-driven/pnpm-lock.yaml +++ b/e2e/tests/ui-driven/pnpm-lock.yaml @@ -6,10 +6,10 @@ settings: dependencies: '@opensystemslab/planx-core': - specifier: git+https://github.com/theopensystemslab/planx-core#af52a96 - version: github.com/theopensystemslab/planx-core/af52a96 + specifier: git+https://github.com/theopensystemslab/planx-core#b8da594 + version: github.com/theopensystemslab/planx-core/b8da594 axios: - specifier: ^1.6.5 + specifier: ^1.6.2 version: 1.6.5 dotenv: specifier: ^16.3.1 @@ -2568,8 +2568,8 @@ packages: resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} dev: false - github.com/theopensystemslab/planx-core/af52a96: - resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/af52a96} + github.com/theopensystemslab/planx-core/b8da594: + resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/b8da594} name: '@opensystemslab/planx-core' version: 1.0.0 prepare: true diff --git a/editor.planx.uk/package.json b/editor.planx.uk/package.json index 498b3c7a89..1fd1f4342a 100644 --- a/editor.planx.uk/package.json +++ b/editor.planx.uk/package.json @@ -14,7 +14,7 @@ "@mui/styles": "^5.15.2", "@mui/utils": "^5.15.2", "@opensystemslab/map": "^0.7.5", - "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#af52a96", + "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#b8da594", "@tiptap/core": "^2.0.3", "@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 ae92776833..1340467a31 100644 --- a/editor.planx.uk/pnpm-lock.yaml +++ b/editor.planx.uk/pnpm-lock.yaml @@ -46,8 +46,8 @@ dependencies: specifier: ^0.7.5 version: 0.7.5 '@opensystemslab/planx-core': - specifier: git+https://github.com/theopensystemslab/planx-core#af52a96 - version: github.com/theopensystemslab/planx-core/af52a96(@types/react@18.2.45) + specifier: git+https://github.com/theopensystemslab/planx-core#b8da594 + version: github.com/theopensystemslab/planx-core/b8da594(@types/react@18.2.45) '@tiptap/core': specifier: ^2.0.3 version: 2.0.3(@tiptap/pm@2.0.3) @@ -21035,9 +21035,9 @@ packages: use-sync-external-store: 1.2.0(react@18.2.0) dev: false - github.com/theopensystemslab/planx-core/af52a96(@types/react@18.2.45): - resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/af52a96} - id: github.com/theopensystemslab/planx-core/af52a96 + github.com/theopensystemslab/planx-core/b8da594(@types/react@18.2.45): + resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/b8da594} + id: github.com/theopensystemslab/planx-core/b8da594 name: '@opensystemslab/planx-core' version: 1.0.0 prepare: true diff --git a/editor.planx.uk/src/@planx/components/Send/Editor.tsx b/editor.planx.uk/src/@planx/components/Send/Editor.tsx index d9e52a1662..58dfee02ff 100644 --- a/editor.planx.uk/src/@planx/components/Send/Editor.tsx +++ b/editor.planx.uk/src/@planx/components/Send/Editor.tsx @@ -73,6 +73,25 @@ const SendComponent: React.FC = (props) => { return originalValues.indexOf(a) - originalValues.indexOf(b); }), ); + + // Show warnings on selection of BOPS or Uniform for likely unsupported services + // Don't actually restrict selection because flowSlug matching is imperfect for some valid test cases + const flowSlug = window.location.pathname?.split("/")?.[1]; + if ( + value === Destination.BOPS && + newCheckedValues.includes(value) && + !["apply-for-a-lawful-development-certificate", "apply-for-prior-approval", "apply-for-planning-permission"].includes(flowSlug) + ) { + alert("BOPS only accepts Lawful Development Certificate, Prior Approval, and Planning Permission submissions. Please do not select if you're building another type of submission service!"); + } + + if ( + value === Destination.Uniform && + newCheckedValues.includes(value) && + flowSlug !== "apply-for-a-lawful-development-certificate" + ) { + alert("Uniform only accepts Lawful Development Certificate submissions. Please do not select if you're building another type of submission service!"); + } }; return ( @@ -109,10 +128,8 @@ const SendComponent: React.FC = (props) => { - API tokens are required to submit successfully to the selected - destinations. Check in with PlanX developers before launching your - service to make sure that tokens are available and configured for - your environment. + API tokens may be required to submit successfully. Check in with PlanX developers before launching your + service to make sure that submissions are configured for your environment. diff --git a/editor.planx.uk/src/@planx/components/Send/model.ts b/editor.planx.uk/src/@planx/components/Send/model.ts index 6debd0f5ca..a296c3dee7 100644 --- a/editor.planx.uk/src/@planx/components/Send/model.ts +++ b/editor.planx.uk/src/@planx/components/Send/model.ts @@ -13,7 +13,7 @@ export interface Send extends MoreInformation { } export const DEFAULT_TITLE = "Send"; -export const DEFAULT_DESTINATION = Destination.BOPS; +export const DEFAULT_DESTINATION = Destination.Email; export const parseContent = (data: Record | undefined): Send => ({ ...parseMoreInformation(data), diff --git a/editor.planx.uk/src/components/Header.test.tsx b/editor.planx.uk/src/components/Header.test.tsx index a10b3b9b9c..e7e7e2dccb 100644 --- a/editor.planx.uk/src/components/Header.test.tsx +++ b/editor.planx.uk/src/components/Header.test.tsx @@ -1,10 +1,10 @@ +import { Team } from "@opensystemslab/planx-core/types"; import { screen } from "@testing-library/react"; import { vanillaStore } from "pages/FlowEditor/lib/store"; import React from "react"; import { act } from "react-dom/test-utils"; import * as ReactNavi from "react-navi"; import { axe, setup } from "testUtils"; -import { Team } from "types"; import flowWithoutSections from "../pages/FlowEditor/lib/__tests__/mocks/flowWithClones.json"; import flowWithThreeSections from "../pages/FlowEditor/lib/__tests__/mocks/flowWithThreeSections.json"; @@ -18,8 +18,9 @@ const mockTeam1: Team = { slug: "opensystemslab", theme: { logo: "logo.jpg", - primary: "#0010A4", - secondary: null, + primaryColour: "#0010A4", + actionColour: "#0010A4", + linkColour: "#0010A4", favicon: null, }, }; diff --git a/editor.planx.uk/src/components/Header.tsx b/editor.planx.uk/src/components/Header.tsx index 861462947c..91d4d0e592 100644 --- a/editor.planx.uk/src/components/Header.tsx +++ b/editor.planx.uk/src/components/Header.tsx @@ -565,7 +565,7 @@ const Header: React.FC = () => { elevation={0} color="transparent" ref={headerRef} - style={{ backgroundColor: theme?.primary || "#2c2c2c" }} + style={{ backgroundColor: theme?.primaryColour || "#2c2c2c" }} > diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings.tsx index ba32841b00..3d759f8b87 100644 --- a/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings.tsx +++ b/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings.tsx @@ -1,5 +1,7 @@ +import Box from "@mui/material/Box"; import Button from "@mui/material/Button"; import Link from "@mui/material/Link"; +import { styled } from "@mui/material/styles"; import Typography from "@mui/material/Typography"; import { useFormik } from "formik"; import { hasFeatureFlag } from "lib/featureFlags"; @@ -15,10 +17,24 @@ import InputRowItem from "ui/shared/InputRowItem"; import InputRowLabel from "ui/shared/InputRowLabel"; import PublicFileUploadButton from "ui/shared/PublicFileUploadButton"; +const DesignPreview = styled(Box)(({ theme }) => ({ + border: `2px solid ${theme.palette.border.input}`, + padding: theme.spacing(2), + boxShadow: "4px 4px 0px rgba(150, 150, 150, 0.5)", +})); + +const exampleColor = "#007078"; + const DesignSettings: React.FC = () => { - const formik = useFormik<{ bgColor: string }>({ + const formik = useFormik<{ + themeColor: string; + buttonColor: string; + linkColor: string; + }>({ initialValues: { - bgColor: "#000", + themeColor: exampleColor, + buttonColor: exampleColor, + linkColor: exampleColor, }, onSubmit: () => {}, validate: () => {}, @@ -27,7 +43,7 @@ const DesignSettings: React.FC = () => { const isUsingFeatureFlag = () => hasFeatureFlag("SHOW_TEAM_SETTINGS"); return ( -
+ <> Design @@ -43,91 +59,226 @@ const DesignSettings: React.FC = () => { ) : ( <> - - Theme colour - - Set the theme colour. The theme colour should be a dark colour - that contrasts with white ("#ffffff"). - - - - See our guide for setting theme colours - - - - - formik.setFieldValue("bgColor", color)} - label="Theme colour" + + + Theme colour & logo + + The theme colour and logo, are used in the header of the + service. The theme colour should be a dark colour that + contrasts with white ("#ffffff"). The logo should contrast + with a dark background colour (your theme colour) and have a + transparent background. + + + + See our guide for setting theme colours and logos + + + + + + formik.setFieldValue("themeColor", color) + } + label="Theme colour" + /> + + + + Logo: + + + + + .png or .svg + + + + + + Preview: + + + council logo - - - + + + + + + + - - Logo - - Set the logo to be used in the header of the service. The logo - should contrast with a dark background colour and have a - transparent background. - - - See our guide for logos - - - Logo: - - - - - .png or .svg +
+ + Button colour + + The button background colour should be either a dark or light + colour. The text will be programatically selected to contrast + with the selected colour (being either black or white). + + + + See our guide for setting button colours + + + + + + formik.setFieldValue("buttonColor", color) + } + label="Button colour" + /> + + + + + + Preview: - - + + + + + + + + +
- - Favicon - - Set the favicon to be used in the browser tab. The favicon - should be 32x32px and in .ico or .png format. - - - - See our guide for favicons - - - - Favicon: - - - - - .ico or .png +
+ + Text link colour + + The text link colour should be a dark colour that contrasts + with white ("#ffffff"). + + + + See our guide for setting text link colours + + + + + + formik.setFieldValue("linkColor", color) + } + label="Text link colour" + /> + + + + + + Preview: - - + + Example text link + + + + + + +
- - + +
+ + Favicon + + Set the favicon to be used in the browser tab. The favicon + should be 32x32px and in .ico or .png format. + + + + See our guide for favicons + + + + Favicon: + + + + + .ico or .png + + + + + + + +
)} - + ); }; diff --git a/editor.planx.uk/src/pages/FlowEditor/lib/analyticsProvider.tsx b/editor.planx.uk/src/pages/FlowEditor/lib/analyticsProvider.tsx index 5595fcad26..506facb7aa 100644 --- a/editor.planx.uk/src/pages/FlowEditor/lib/analyticsProvider.tsx +++ b/editor.planx.uk/src/pages/FlowEditor/lib/analyticsProvider.tsx @@ -20,6 +20,11 @@ type AnalyticsLogDirection = | "reset" | "save"; +const ALLOW_LIST = [ + "proposal.projectType", + "application.declaration.connection", +] as const; + export type HelpClickMetadata = Record; export type SelectedUrlsMetadata = Record<"selectedUrls", string[]>; export type BackwardsNavigationInitiatorType = "change" | "back"; @@ -102,21 +107,23 @@ export const AnalyticsProvider: React.FC<{ children: React.ReactNode }> = ({ const previousBreadcrumbs = usePrevious(breadcrumbs); const trackVisibilityChange = () => { - if (lastVisibleNodeAnalyticsLogId && shouldTrackAnalytics) { - if (document.visibilityState === "hidden") { + if (shouldSkipTracking()) return; + + switch (document.visibilityState) { + case "hidden": send( `${ process.env.REACT_APP_API_URL - }/analytics/log-user-exit?analyticsLogId=${lastVisibleNodeAnalyticsLogId.toString()}`, + }/analytics/log-user-exit?analyticsLogId=${lastVisibleNodeAnalyticsLogId?.toString()}`, ); - } - if (document.visibilityState === "visible") { + break; + case "visible": send( `${ process.env.REACT_APP_API_URL }/analytics/log-user-resume?analyticsLogId=${lastVisibleNodeAnalyticsLogId?.toString()}`, ); - } + break; } }; @@ -132,7 +139,8 @@ export const AnalyticsProvider: React.FC<{ children: React.ReactNode }> = ({ useEffect(onVisibilityChange, []); useEffect(() => { - if (shouldTrackAnalytics && analyticsId) trackAutoTrueNodes(); + if (!shouldTrackAnalytics || !analyticsId) return; + trackBreadcrumbChanges(); }, [breadcrumbs]); return ( @@ -168,6 +176,7 @@ export const AnalyticsProvider: React.FC<{ children: React.ReactNode }> = ({ const metadata: NodeMetadata = getNodeMetadata(nodeToTrack, nodeId); const nodeType = nodeToTrack?.type ? TYPES[nodeToTrack.type] : null; const nodeTitle = extractNodeTitle(nodeToTrack); + const nodeFn = nodeToTrack?.data?.fn || nodeToTrack?.data?.val; const result = await insertNewAnalyticsLog( logDirection, @@ -176,6 +185,7 @@ export const AnalyticsProvider: React.FC<{ children: React.ReactNode }> = ({ nodeType, nodeTitle, nodeId, + nodeFn, ); const { id, created_at: newLogCreatedAt } = @@ -208,6 +218,7 @@ export const AnalyticsProvider: React.FC<{ children: React.ReactNode }> = ({ nodeType: string | null, nodeTitle: string, nodeId: string | null, + nodeFn: string | null, ) { const result = await publicClient.mutate({ mutation: gql` @@ -218,6 +229,7 @@ export const AnalyticsProvider: React.FC<{ children: React.ReactNode }> = ({ $node_type: String $node_title: String $node_id: String + $node_fn: String ) { insert_analytics_logs_one( object: { @@ -228,6 +240,7 @@ export const AnalyticsProvider: React.FC<{ children: React.ReactNode }> = ({ node_type: $node_type node_title: $node_title node_id: $node_id + node_fn: $node_fn } ) { id @@ -242,6 +255,7 @@ export const AnalyticsProvider: React.FC<{ children: React.ReactNode }> = ({ node_type: nodeType, node_title: nodeTitle, node_id: nodeId, + node_fn: nodeFn, }, }); return result; @@ -272,142 +286,190 @@ export const AnalyticsProvider: React.FC<{ children: React.ReactNode }> = ({ }); } + function shouldSkipTracking() { + return (!shouldTrackAnalytics || !lastVisibleNodeAnalyticsLogId) + } + async function trackHelpClick(metadata?: HelpClickMetadata) { - if (shouldTrackAnalytics && lastVisibleNodeAnalyticsLogId) { - await publicClient.mutate({ - mutation: gql` - mutation UpdateHasClickedHelp($id: bigint!, $metadata: jsonb = {}) { - update_analytics_logs_by_pk( - pk_columns: { id: $id } - _set: { has_clicked_help: true } - _append: { metadata: $metadata } - ) { - id - } + if (shouldSkipTracking()) return; + + await publicClient.mutate({ + mutation: gql` + mutation UpdateHasClickedHelp($id: bigint!, $metadata: jsonb = {}) { + update_analytics_logs_by_pk( + pk_columns: { id: $id } + _set: { has_clicked_help: true } + _append: { metadata: $metadata } + ) { + id } - `, - variables: { - id: lastVisibleNodeAnalyticsLogId, - metadata, - }, - }); - } + } + `, + variables: { + id: lastVisibleNodeAnalyticsLogId, + metadata, + }, + }); } async function trackNextStepsLinkClick(metadata?: SelectedUrlsMetadata) { - if (shouldTrackAnalytics && lastVisibleNodeAnalyticsLogId) { - await publicClient.mutate({ - mutation: gql` - mutation UpdateHasClickNextStepsLink( - $id: bigint! - $metadata: jsonb = {} + if (shouldSkipTracking()) return; + + await publicClient.mutate({ + mutation: gql` + mutation UpdateHasClickNextStepsLink( + $id: bigint! + $metadata: jsonb = {} + ) { + update_analytics_logs_by_pk( + pk_columns: { id: $id } + _append: { metadata: $metadata } ) { - update_analytics_logs_by_pk( - pk_columns: { id: $id } - _append: { metadata: $metadata } - ) { - id - } + id } - `, - variables: { - id: lastVisibleNodeAnalyticsLogId, - metadata, - }, - }); - } + } + `, + variables: { + id: lastVisibleNodeAnalyticsLogId, + metadata, + }, + }); } async function trackFlowDirectionChange( flowDirection: AnalyticsLogDirection, ) { - if (shouldTrackAnalytics && lastVisibleNodeAnalyticsLogId) { - await publicClient.mutate({ - mutation: gql` - mutation UpdateFlowDirection($id: bigint!, $flow_direction: String) { - update_analytics_logs_by_pk( - pk_columns: { id: $id } - _set: { flow_direction: $flow_direction } - ) { - id - } + if (shouldSkipTracking()) return; + + await publicClient.mutate({ + mutation: gql` + mutation UpdateFlowDirection($id: bigint!, $flow_direction: String) { + update_analytics_logs_by_pk( + pk_columns: { id: $id } + _set: { flow_direction: $flow_direction } + ) { + id } - `, - variables: { - id: lastVisibleNodeAnalyticsLogId, - flow_direction: flowDirection, - }, - }); - } + } + `, + variables: { + id: lastVisibleNodeAnalyticsLogId, + flow_direction: flowDirection, + }, + }); } async function trackBackwardsNavigation( initiator: BackwardsNavigationInitiatorType, nodeId?: string, ) { + if (shouldSkipTracking()) return; + const targetNodeMetadata = nodeId ? getTargetNodeDataFromFlow(nodeId) : {}; const metadata: Record = {}; metadata[`${initiator}`] = targetNodeMetadata; - if (shouldTrackAnalytics && lastVisibleNodeAnalyticsLogId) { - await publicClient.mutate({ - mutation: gql` - mutation UpdateHaInitiatedBackwardsNavigation( - $id: bigint! - $metadata: jsonb = {} + await publicClient.mutate({ + mutation: gql` + mutation UpdateHaInitiatedBackwardsNavigation( + $id: bigint! + $metadata: jsonb = {} + ) { + update_analytics_logs_by_pk( + pk_columns: { id: $id } + _append: { metadata: $metadata } ) { - update_analytics_logs_by_pk( - pk_columns: { id: $id } - _append: { metadata: $metadata } - ) { - id - } + id } - `, - variables: { - id: lastVisibleNodeAnalyticsLogId, - metadata, - }, - }); - } + } + `, + variables: { + id: lastVisibleNodeAnalyticsLogId, + metadata, + }, + }); } async function createAnalytics(type: AnalyticsType) { - if (shouldTrackAnalytics) { - const userAgent = Bowser.parse(window.navigator.userAgent); - const referrer = document.referrer || null; - - const response = await publicClient.mutate({ - mutation: gql` - mutation InsertNewAnalytics( - $type: String - $flow_id: uuid - $user_agent: jsonb - $referrer: String + if (!shouldTrackAnalytics) return; + const userAgent = Bowser.parse(window.navigator.userAgent); + const referrer = document.referrer || null; + + const response = await publicClient.mutate({ + mutation: gql` + mutation InsertNewAnalytics( + $type: String + $flow_id: uuid + $user_agent: jsonb + $referrer: String + ) { + insert_analytics_one( + object: { + type: $type + flow_id: $flow_id + user_agent: $user_agent + referrer: $referrer + } + ) { + id + } + } + `, + variables: { + type, + flow_id: flowId, + user_agent: userAgent, + referrer, + }, + }); + const id = response.data.insert_analytics_one.id; + setAnalyticsId(id); + const currentNodeId = currentCard()?.id; + if (currentNodeId) track(currentNodeId, type, id); + } + + async function updateLastVisibleNodeLogWithAllowListAnswers(nodeId: string) { + if (shouldSkipTracking()) return; + + + const allowListAnswers = getAllowListAnswers(nodeId); + if (!allowListAnswers) return; + + await publicClient.mutate({ + mutation: gql` + mutation UpdateAllowListAnswers( + $id: bigint! + $allow_list_answers: jsonb + $node_id: String! + ) { + update_analytics_logs( + where: { id: { _eq: $id }, node_id: { _eq: $node_id } } + _set: { allow_list_answers: $allow_list_answers } ) { - insert_analytics_one( - object: { - type: $type - flow_id: $flow_id - user_agent: $user_agent - referrer: $referrer - } - ) { + returning { id } } - `, - variables: { - type, - flow_id: flowId, - user_agent: userAgent, - referrer, - }, + } + `, + variables: { + id: lastVisibleNodeAnalyticsLogId, + allow_list_answers: allowListAnswers, + node_id: nodeId, + }, + }); + } + + function getAllowListAnswers(nodeId: string) { + const { data } = flow[nodeId]; + const nodeFn = data?.fn || data?.val; + if (nodeFn && ALLOW_LIST.includes(nodeFn)) { + const answerIds = breadcrumbs[nodeId]?.answers; + const answerValues = answerIds?.map((answerId) => { + return flow[answerId]?.data?.val; }); - const id = response.data.insert_analytics_one.id; - setAnalyticsId(id); - const currentNodeId = currentCard()?.id; - if (currentNodeId) track(currentNodeId, type, id); + const filteredAnswers = answerValues?.filter(Boolean); + return filteredAnswers; } } @@ -447,24 +509,24 @@ export const AnalyticsProvider: React.FC<{ children: React.ReactNode }> = ({ * Capture user input errors caught by ErrorWrapper component */ async function trackInputErrors(error: string) { - if (shouldTrackAnalytics && lastVisibleNodeAnalyticsLogId) { - await publicClient.mutate({ - mutation: gql` - mutation TrackInputErrors($id: bigint!, $error: jsonb) { - update_analytics_logs_by_pk( - pk_columns: { id: $id } - _append: { input_errors: $error } - ) { - id - } + if (shouldSkipTracking()) return; + + await publicClient.mutate({ + mutation: gql` + mutation TrackInputErrors($id: bigint!, $error: jsonb) { + update_analytics_logs_by_pk( + pk_columns: { id: $id } + _append: { input_errors: $error } + ) { + id } - `, - variables: { - id: lastVisibleNodeAnalyticsLogId, - error, - }, - }); - } + } + `, + variables: { + id: lastVisibleNodeAnalyticsLogId, + error, + }, + }); } function extractNodeTitle(node: Store.node) { @@ -476,36 +538,37 @@ export const AnalyticsProvider: React.FC<{ children: React.ReactNode }> = ({ } function determineLogDirection() { - if (previousBreadcrumbs) { - const curLength = Object.keys(breadcrumbs).length; - const prevLength = Object.keys(previousBreadcrumbs).length; - if (curLength > prevLength) return "forwards"; - if (curLength < prevLength) return "backwards"; - } + if (!previousBreadcrumbs) return; + + const curLength = Object.keys(breadcrumbs).length; + const prevLength = Object.keys(previousBreadcrumbs).length; + if (curLength > prevLength) return "forwards"; + if (curLength < prevLength) return "backwards"; } function findUpdatedBreadcrumbKeys(): string[] | undefined { - if (previousBreadcrumbs) { - const currentKeys = Object.keys(breadcrumbs); - const previousKeys = Object.keys(previousBreadcrumbs); + if (!previousBreadcrumbs) return; - const updatedBreadcrumbKeys = currentKeys.filter( - (breadcrumb) => !previousKeys.includes(breadcrumb), - ); - return updatedBreadcrumbKeys; - } + const currentKeys = Object.keys(breadcrumbs); + const previousKeys = Object.keys(previousBreadcrumbs); + const updatedBreadcrumbKeys = currentKeys.filter( + (breadcrumb) => !previousKeys.includes(breadcrumb), + ); + return updatedBreadcrumbKeys; } - function trackAutoTrueNodes() { + function trackBreadcrumbChanges() { const updatedBreadcrumbKeys = findUpdatedBreadcrumbKeys(); - if (updatedBreadcrumbKeys) { - updatedBreadcrumbKeys.forEach((breadcrumbKey) => { - const breadcrumb = breadcrumbs[breadcrumbKey]; - if (breadcrumb.auto) { - track(breadcrumbKey); - } - }); - } + if (!updatedBreadcrumbKeys) return; + + updatedBreadcrumbKeys.forEach((breadcrumbKey) => { + const breadcrumb = breadcrumbs[breadcrumbKey]; + if (breadcrumb.auto) { + track(breadcrumbKey); + } else { + updateLastVisibleNodeLogWithAllowListAnswers(breadcrumbKey); + } + }); } }; diff --git a/editor.planx.uk/src/pages/FlowEditor/lib/store/team.ts b/editor.planx.uk/src/pages/FlowEditor/lib/store/team.ts index 8eeaed720d..57162d0b34 100644 --- a/editor.planx.uk/src/pages/FlowEditor/lib/store/team.ts +++ b/editor.planx.uk/src/pages/FlowEditor/lib/store/team.ts @@ -1,9 +1,11 @@ -import { GeoJSONObject } from "@turf/helpers"; +import { + NotifyPersonalisation, + Team, + TeamSettings, + TeamTheme, +} from "@opensystemslab/planx-core/types"; import gql from "graphql-tag"; import { client } from "lib/graphql"; -import { NotifyPersonalisation, TeamSettings } from "types"; -import { TeamTheme } from "types"; -import { Team } from "types"; import type { StateCreator } from "zustand"; export interface TeamStore { @@ -13,7 +15,7 @@ export interface TeamStore { teamSettings?: TeamSettings; teamSlug: string; notifyPersonalisation?: NotifyPersonalisation; - boundaryBBox?: GeoJSONObject; + boundaryBBox?: Team["boundaryBBox"]; setTeam: (team: Team) => void; getTeam: () => Team; diff --git a/editor.planx.uk/src/pages/FlowEditor/lib/store/user.ts b/editor.planx.uk/src/pages/FlowEditor/lib/store/user.ts index 10b0f3e00a..d4c1345bf8 100644 --- a/editor.planx.uk/src/pages/FlowEditor/lib/store/user.ts +++ b/editor.planx.uk/src/pages/FlowEditor/lib/store/user.ts @@ -1,7 +1,6 @@ -import { User, UserTeams } from "@opensystemslab/planx-core/types"; +import { Team, User, UserTeams } from "@opensystemslab/planx-core/types"; import axios from "axios"; import { _client } from "client"; -import { Team } from "types"; import type { StateCreator } from "zustand"; export interface UserStore { diff --git a/editor.planx.uk/src/pages/Teams.tsx b/editor.planx.uk/src/pages/Teams.tsx index a33dd46b40..ebc9a2cdcc 100644 --- a/editor.planx.uk/src/pages/Teams.tsx +++ b/editor.planx.uk/src/pages/Teams.tsx @@ -4,10 +4,10 @@ import Box from "@mui/material/Box"; import Card from "@mui/material/Card"; 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 type { Team } from "../types"; import { useStore } from "./FlowEditor/lib/store"; interface Props { diff --git a/editor.planx.uk/src/pages/layout/PublicLayout.tsx b/editor.planx.uk/src/pages/layout/PublicLayout.tsx index 8e7a505c9e..9aa2c552b9 100644 --- a/editor.planx.uk/src/pages/layout/PublicLayout.tsx +++ b/editor.planx.uk/src/pages/layout/PublicLayout.tsx @@ -88,8 +88,8 @@ const PublicFooter: React.FC = () => { }; const PublicLayout: React.FC = ({ children }) => { - const teamPrimaryColor = useStore((state) => state.teamTheme?.primary); - const teamMUITheme = generateTeamTheme(teamPrimaryColor); + const teamTheme = useStore((state) => state.teamTheme); + const teamMUITheme = generateTeamTheme(teamTheme); return ( diff --git a/editor.planx.uk/src/routes/views/published.tsx b/editor.planx.uk/src/routes/views/published.tsx index f583bfcedc..f61cb39b7b 100644 --- a/editor.planx.uk/src/routes/views/published.tsx +++ b/editor.planx.uk/src/routes/views/published.tsx @@ -72,8 +72,9 @@ const fetchDataForPublishedView = async ( id team { theme { - primary: primary_colour - secondary: secondary_colour + primaryColour: primary_colour + actionColour: action_colour + linkColour: link_colour logo favicon } diff --git a/editor.planx.uk/src/routes/views/standalone.tsx b/editor.planx.uk/src/routes/views/standalone.tsx index 71452b5033..1f8780ff54 100644 --- a/editor.planx.uk/src/routes/views/standalone.tsx +++ b/editor.planx.uk/src/routes/views/standalone.tsx @@ -60,8 +60,9 @@ const fetchDataForStandaloneView = async ( id team { theme { - primary: primary_colour - secondary: secondary_colour + primaryColour: primary_colour + actionColour: action_colour + linkColour: link_colour logo favicon } diff --git a/editor.planx.uk/src/routes/views/unpublished.tsx b/editor.planx.uk/src/routes/views/unpublished.tsx index 1495df6059..fe212e3ab5 100644 --- a/editor.planx.uk/src/routes/views/unpublished.tsx +++ b/editor.planx.uk/src/routes/views/unpublished.tsx @@ -63,8 +63,9 @@ const fetchDataForUnpublishedView = async ( id team { theme { - primary: primary_colour - secondary: secondary_colour + primaryColour: primary_colour + actionColour: action_colour + linkColour: link_colour logo favicon } diff --git a/editor.planx.uk/src/theme.ts b/editor.planx.uk/src/theme.ts index 726669c80c..3d383921b7 100644 --- a/editor.planx.uk/src/theme.ts +++ b/editor.planx.uk/src/theme.ts @@ -13,8 +13,11 @@ import createPalette, { PaletteOptions, } from "@mui/material/styles/createPalette"; import { deepmerge } from "@mui/utils"; +import { TeamTheme } from "@opensystemslab/planx-core/types"; +import { getContrastTextColor } from "styleUtils"; const DEFAULT_PRIMARY_COLOR = "#0010A4"; +const DEFAULT_TONAL_OFFSET = 0.2; // Type styles export const FONT_WEIGHT_SEMI_BOLD = "600"; @@ -38,6 +41,15 @@ const DEFAULT_PALETTE: Partial = { primary: "#0B0C0C", secondary: "#505A5F", }, + link: { + main: DEFAULT_PRIMARY_COLOR + }, + prompt: { + main: DEFAULT_PRIMARY_COLOR, + contrastText: "#FFFFFF", + light: lighten(DEFAULT_PRIMARY_COLOR, DEFAULT_TONAL_OFFSET), + dark: darken(DEFAULT_PRIMARY_COLOR, DEFAULT_TONAL_OFFSET), + }, action: { selected: "#F8F8F8", focus: "#FFDD00", @@ -57,6 +69,7 @@ const DEFAULT_PALETTE: Partial = { input: "#0B0C0C", light: "#E0E0E0", }, + tonalOffset: DEFAULT_TONAL_OFFSET, }; // GOVUK Focus style @@ -85,8 +98,8 @@ export const borderedFocusStyle = { background: "transparent", }; -export const linkStyle = (primaryColor?: string) => ({ - color: primaryColor || "inherit", +export const linkStyle = (linkColour: string) => ({ + color: linkColour || "inherit", textDecoration: "underline", textDecorationThickness: "1px", textUnderlineOffset: "0.1em", @@ -100,10 +113,19 @@ export const linkStyle = (primaryColor?: string) => ({ "&:focus-visible": focusStyle, }); -const getThemeOptions = (primaryColor: string): ThemeOptions => { +const getThemeOptions = ({ primaryColour, linkColour, actionColour}: TeamTheme): ThemeOptions => { const teamPalette: Partial = { primary: { - main: primaryColor, + main: primaryColour, + }, + link: { + main: linkColour, + }, + prompt: { + main: actionColour, + light: lighten(actionColour, DEFAULT_TONAL_OFFSET), + dark: darken(actionColour, DEFAULT_TONAL_OFFSET), + contrastText: getContrastTextColor(actionColour, "#FFF")!, }, }; const palette = createPalette(deepmerge(DEFAULT_PALETTE, teamPalette)); @@ -337,7 +359,7 @@ const getThemeOptions = (primaryColor: string): ThemeOptions => { MuiLink: { styleOverrides: { root: { - ...linkStyle(palette.primary.main), + ...linkStyle(palette.link.main), "&:disabled": { color: palette.text.disabled, cursor: "default", @@ -430,14 +452,20 @@ const getThemeOptions = (primaryColor: string): ThemeOptions => { // Generate a MUI theme based on a team's primary color const generateTeamTheme = ( - primaryColor: string = DEFAULT_PRIMARY_COLOR, + teamTheme: TeamTheme = { + primaryColour: DEFAULT_PRIMARY_COLOR, + actionColour: DEFAULT_PRIMARY_COLOR, + linkColour: DEFAULT_PRIMARY_COLOR, + logo: null, + favicon: null, + }, ): MUITheme => { - const themeOptions = getThemeOptions(primaryColor); + const themeOptions = getThemeOptions(teamTheme); const theme = responsiveFontSizes(createTheme(themeOptions), { factor: 3 }); return theme; }; // A static MUI theme based on PlanX's default palette -const defaultTheme = generateTeamTheme(DEFAULT_PRIMARY_COLOR); +const defaultTheme = generateTeamTheme(); export { defaultTheme, generateTeamTheme }; diff --git a/editor.planx.uk/src/themeOverrides.d.ts b/editor.planx.uk/src/themeOverrides.d.ts index d779e08863..e849b3bda4 100644 --- a/editor.planx.uk/src/themeOverrides.d.ts +++ b/editor.planx.uk/src/themeOverrides.d.ts @@ -25,10 +25,14 @@ declare module "@mui/material/styles" { declare module "@mui/material/styles/createPalette" { interface Palette { border: { main: string; input: string; light: string }; + link: { main: string; }; + prompt: { main: string; contrastText: string; light: string; dark: string; }; } interface PaletteOptions { border?: { main: string; input: string; light: string }; + link?: { main: string; }; + prompt?: { main: string; contrastText: string; light: string; dark: string; }; } } @@ -37,4 +41,8 @@ declare module "@mui/material/Button" { interface ButtonPropsVariantOverrides { help: true; } + + interface ButtonPropsColorOverrides { + prompt: true; + } } diff --git a/editor.planx.uk/src/types.ts b/editor.planx.uk/src/types.ts index 63450fe9fa..3d8a63117f 100644 --- a/editor.planx.uk/src/types.ts +++ b/editor.planx.uk/src/types.ts @@ -1,5 +1,4 @@ -import { GovUKPayment } from "@opensystemslab/planx-core/types"; -import { GeoJSONObject } from "@turf/helpers"; +import { GovUKPayment, Team } from "@opensystemslab/planx-core/types"; import { useFormik } from "formik"; import { Store } from "./pages/FlowEditor/lib/store/index"; @@ -15,45 +14,6 @@ export interface Flow { team: Team; settings?: FlowSettings; } - -export interface Team { - id: number; - name: string; - slug: string; - settings?: TeamSettings; - theme?: TeamTheme; - notifyPersonalisation?: NotifyPersonalisation; - boundaryBBox?: GeoJSONObject; -} - -export interface TeamTheme { - primary: string; - secondary: string | null; - logo: string | null; - favicon: string | null; -} - -export interface TeamSettings { - design?: { - color?: string; - }; - homepage?: string; - externalPlanningSite: { - name: string; - url: string; - }; - supportEmail?: string; - boundary?: string; - hasPlanningData?: boolean; -} - -export interface NotifyPersonalisation { - helpEmail: string; - helpPhone: string; - emailReplyToId: string; - helpOpeningHours: string; -} - export interface GlobalSettings { footerContent?: { [key: string]: TextContent }; } diff --git a/editor.planx.uk/src/ui/editor/EditorRow.tsx b/editor.planx.uk/src/ui/editor/EditorRow.tsx index 63dc188bd1..f489337596 100644 --- a/editor.planx.uk/src/ui/editor/EditorRow.tsx +++ b/editor.planx.uk/src/ui/editor/EditorRow.tsx @@ -16,7 +16,7 @@ const Root = styled(Box, { "&:first-of-type": { paddingTop: 0, }, - "& > * + *": { + "& > * + *, & > form > * + *": { ...contentFlowSpacing(theme), }, ...(background && { @@ -27,7 +27,7 @@ const Root = styled(Box, { }), })); -export default function InputRow({ +export default function EditorRow({ children, background, }: { diff --git a/editor.planx.uk/src/ui/public/ExternalPlanningSiteDialog.tsx b/editor.planx.uk/src/ui/public/ExternalPlanningSiteDialog.tsx index 4a450a5ced..27a9d5c256 100644 --- a/editor.planx.uk/src/ui/public/ExternalPlanningSiteDialog.tsx +++ b/editor.planx.uk/src/ui/public/ExternalPlanningSiteDialog.tsx @@ -6,10 +6,10 @@ import DialogTitle from "@mui/material/DialogTitle"; import Link from "@mui/material/Link"; import Typography from "@mui/material/Typography"; import { visuallyHidden } from "@mui/utils"; +import { TeamSettings } from "@opensystemslab/planx-core/types"; import { useStore } from "pages/FlowEditor/lib/store"; import React from "react"; import { useState } from "react"; -import { TeamSettings } from "types"; export enum DialogPurpose { MissingProjectType, diff --git a/editor.planx.uk/src/ui/shared/ReactMarkdownOrHtml.tsx b/editor.planx.uk/src/ui/shared/ReactMarkdownOrHtml.tsx index cf228f9a07..0a3c0f7420 100644 --- a/editor.planx.uk/src/ui/shared/ReactMarkdownOrHtml.tsx +++ b/editor.planx.uk/src/ui/shared/ReactMarkdownOrHtml.tsx @@ -6,7 +6,7 @@ import ReactMarkdown from "react-markdown"; import { FONT_WEIGHT_SEMI_BOLD, linkStyle } from "theme"; const styles = (theme: Theme) => ({ - "& a": linkStyle(theme.palette.primary.main), + "& a": linkStyle(theme.palette.link.main), "& h1": theme.typography.h2, "& h2": theme.typography.h3, "& h3": theme.typography.h3, diff --git a/hasura.planx.uk/metadata/tables.yaml b/hasura.planx.uk/metadata/tables.yaml index 24e17de3b8..d4880de914 100644 --- a/hasura.planx.uk/metadata/tables.yaml +++ b/hasura.planx.uk/metadata/tables.yaml @@ -40,6 +40,7 @@ - id - input_errors - metadata + - node_fn - node_id - node_title - node_type @@ -51,12 +52,14 @@ - analytics_id - created_at - id + - node_id - user_exit filter: {} update_permissions: - role: public permission: columns: + - allow_list_answers - flow_direction - has_clicked_help - input_errors @@ -1373,65 +1376,71 @@ - role: api permission: columns: - - id - - team_id + - action_colour - favicon + - id + - link_colour - logo - primary_colour - - secondary_colour + - team_id filter: {} comment: "" - role: platformAdmin permission: columns: - - id - - team_id + - action_colour - favicon + - id + - link_colour - logo - primary_colour - - secondary_colour + - team_id filter: {} comment: "" - role: public permission: columns: - - id - - team_id + - action_colour - favicon + - id + - link_colour - logo - primary_colour - - secondary_colour + - team_id filter: {} comment: "" - role: teamEditor permission: columns: - - id - - team_id + - action_colour - favicon + - id + - link_colour - logo - primary_colour - - secondary_colour + - team_id filter: {} comment: "" update_permissions: - role: platformAdmin permission: columns: + - action_colour - favicon + - link_colour - logo - primary_colour - - secondary_colour filter: {} check: null comment: "" - role: teamEditor permission: columns: + - action_colour - favicon + - link_colour - logo - primary_colour - - secondary_colour filter: {} check: null comment: "" diff --git a/hasura.planx.uk/migrations/1704731606083_squashed/down.sql b/hasura.planx.uk/migrations/1704731606083_squashed/down.sql new file mode 100644 index 0000000000..7f022dc747 --- /dev/null +++ b/hasura.planx.uk/migrations/1704731606083_squashed/down.sql @@ -0,0 +1,8 @@ + +comment on column "public"."analytics_logs"."allow_list_answers" is NULL; + +comment on column "public"."analytics_logs"."node_fn" is NULL; + +alter table "public"."analytics_logs" drop column "node_fn"; + +alter table "public"."analytics_logs" drop column "allow_list_answers"; \ No newline at end of file diff --git a/hasura.planx.uk/migrations/1704731606083_squashed/up.sql b/hasura.planx.uk/migrations/1704731606083_squashed/up.sql new file mode 100644 index 0000000000..cef41e62f7 --- /dev/null +++ b/hasura.planx.uk/migrations/1704731606083_squashed/up.sql @@ -0,0 +1,10 @@ + +alter table "public"."analytics_logs" add column "node_fn" text + null; + +alter table "public"."analytics_logs" add column "allow_list_answers" JSONB + null default '[]'::jsonb; + +comment on column "public"."analytics_logs"."node_fn" is E'The passport variable a node can relate to as stored on the `fn` or `val` of the node'; + +comment on column "public"."analytics_logs"."allow_list_answers" is E'If the node sets a passport variable deemed as safe to track then any answers are stored in this field'; diff --git a/hasura.planx.uk/migrations/1704892559414_alter_table_public_team_themes_add_column_link_colour/down.sql b/hasura.planx.uk/migrations/1704892559414_alter_table_public_team_themes_add_column_link_colour/down.sql new file mode 100644 index 0000000000..696e72f8ae --- /dev/null +++ b/hasura.planx.uk/migrations/1704892559414_alter_table_public_team_themes_add_column_link_colour/down.sql @@ -0,0 +1,3 @@ +alter table "public"."team_themes" add column "secondary_colour" text; + +comment on column "public"."team_themes"."secondary_colour" is E'Must be hex triplet (e.g. #112233)'; \ No newline at end of file diff --git a/hasura.planx.uk/migrations/1704892559414_alter_table_public_team_themes_add_column_link_colour/up.sql b/hasura.planx.uk/migrations/1704892559414_alter_table_public_team_themes_add_column_link_colour/up.sql new file mode 100644 index 0000000000..37dd213d4d --- /dev/null +++ b/hasura.planx.uk/migrations/1704892559414_alter_table_public_team_themes_add_column_link_colour/up.sql @@ -0,0 +1,10 @@ +alter table "public"."team_themes" drop column "secondary_colour" cascade; + +alter table "public"."team_themes" add column "link_colour" text + not null default '#0010A4'; +alter table "public"."team_themes" add column "action_colour" text + not null default '#0010A4'; + + +comment on column "public"."team_themes"."link_colour" is E'Must be hex triplet (e.g. #112233)'; +comment on column "public"."team_themes"."action_colour" is E'Must be hex triplet (e.g. #112233)'; diff --git a/scripts/seed-database/write/team_themes.sql b/scripts/seed-database/write/team_themes.sql index 3060ef58ff..432886d346 100644 --- a/scripts/seed-database/write/team_themes.sql +++ b/scripts/seed-database/write/team_themes.sql @@ -1,3 +1,5 @@ +-- TODO: Update to add action_color and link_colour columns + -- insert teams_themes overwriting conflicts CREATE TEMPORARY TABLE sync_team_themes ( id integer, @@ -15,7 +17,7 @@ INSERT INTO id, team_id, primary_colour, - secondary_colour, + -- secondary_colour, logo, favicon ) @@ -23,15 +25,16 @@ SELECT id, team_id, primary_colour, - secondary_colour, + -- secondary_colour, logo, favicon FROM sync_team_themes ON CONFLICT (id) DO UPDATE SET + team_id = EXCLUDED.team_id, primary_colour = EXCLUDED.primary_colour, - secondary_colour = EXCLUDED.secondary_colour, + -- secondary_colour = EXCLUDED.secondary_colour, logo = EXCLUDED.logo, favicon = EXCLUDED.favicon; SELECT