From dee5e1a85d6b7ce41d6c592bdb34cf1b86ce7ed7 Mon Sep 17 00:00:00 2001 From: Jessica McInchak Date: Wed, 13 Nov 2024 13:45:40 +0100 Subject: [PATCH 1/6] chore: add formatting to Slack notifications if meets pilot testing criteria (#3944) --- .../service/sendNotification/index.ts | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/api.planx.uk/modules/webhooks/service/sendNotification/index.ts b/api.planx.uk/modules/webhooks/service/sendNotification/index.ts index 0fa5aa221b..c6ff6409b5 100644 --- a/api.planx.uk/modules/webhooks/service/sendNotification/index.ts +++ b/api.planx.uk/modules/webhooks/service/sendNotification/index.ts @@ -23,7 +23,40 @@ export const sendSlackNotification = async ( if (disability) message += " [Exempt]"; if (resubmission) message += " [Resubmission]"; - await slack.send(":incoming_envelope: " + message); + // Prefix message with a custom emoji if this submission fits November 2024 ODP Pilot testing criteria + const pilotCouncils = [ + "barnet", + "buckinghamshire", + "camden", + "lambeth", + "medway", + ]; + // Message app types are a bit messy - Uniform won't have app type at all so we id by system because only accepts LDCs, + // BOPS will have internal ID with app type prefix, Email & S3 will have full PlanX service name + const pilotServices = [ + "uniform", + "happ", + "ldc", + "apply for planning permission", + "apply for a lawful development certificate", + ]; + + let isPilotEvent = false; + pilotCouncils.forEach((council) => { + pilotServices.forEach((service) => { + if ( + message?.toLowerCase()?.includes(council) && + message?.toLowerCase()?.includes(service) + ) { + isPilotEvent = true; + } + }); + }); + + const baseMessage = ":incoming_envelope: " + message; + message = isPilotEvent ? ":large_orange_square: " + baseMessage : baseMessage; + + await slack.send(message); return message; }; From 98e181e7f06cb240145df3621342e4f8a9b30ed5 Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Wed, 13 Nov 2024 14:34:25 +0000 Subject: [PATCH 2/6] feat: Display feedback score in feedback log (#3942) --- .../Editor/FeedbackEditor.stories.tsx | 2 +- .../src/components/Feedback/types.ts | 1 + .../Flow/FeedbackLog/FeedbackLog.tsx | 3 ++ .../FeedbackLog/components/CollapsibleRow.tsx | 12 ++++++++ editor.planx.uk/src/routes/feedback.tsx | 8 +++-- hasura.planx.uk/metadata/tables.yaml | 27 +++++++++-------- .../1731495319577_run_sql_migration/down.sql | 29 +++++++++++++++++++ .../1731495319577_run_sql_migration/up.sql | 26 +++++++++++++++++ 8 files changed, 92 insertions(+), 16 deletions(-) create mode 100644 hasura.planx.uk/migrations/1731495319577_run_sql_migration/down.sql create mode 100644 hasura.planx.uk/migrations/1731495319577_run_sql_migration/up.sql diff --git a/editor.planx.uk/src/@planx/components/Feedback/Editor/FeedbackEditor.stories.tsx b/editor.planx.uk/src/@planx/components/Feedback/Editor/FeedbackEditor.stories.tsx index 238eedb3bd..241842dafd 100644 --- a/editor.planx.uk/src/@planx/components/Feedback/Editor/FeedbackEditor.stories.tsx +++ b/editor.planx.uk/src/@planx/components/Feedback/Editor/FeedbackEditor.stories.tsx @@ -4,7 +4,7 @@ import React from "react"; import { FeedbackEditor } from "./Editor"; const meta = { - title: "Editor Components/Feedback", + title: "Editor Components/Feedback modal", component: FeedbackEditor, } satisfies Meta; diff --git a/editor.planx.uk/src/components/Feedback/types.ts b/editor.planx.uk/src/components/Feedback/types.ts index 3b9adb9784..66babe66c8 100644 --- a/editor.planx.uk/src/components/Feedback/types.ts +++ b/editor.planx.uk/src/components/Feedback/types.ts @@ -21,6 +21,7 @@ export type FeedbackCategory = | "comment" | "inaccuracy" | "component"; + export type FeedbackView = "banner" | "triage" | FeedbackCategory | "thanks"; export type ClickEvents = "close" | "back" | "triage" | FeedbackCategory; diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Flow/FeedbackLog/FeedbackLog.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Flow/FeedbackLog/FeedbackLog.tsx index 473bbe19e1..17a9ee030b 100644 --- a/editor.planx.uk/src/pages/FlowEditor/components/Flow/FeedbackLog/FeedbackLog.tsx +++ b/editor.planx.uk/src/pages/FlowEditor/components/Flow/FeedbackLog/FeedbackLog.tsx @@ -52,6 +52,9 @@ export const FeedbackLog: React.FC = ({ feedback }) => { Date + + Rating + Comment diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Flow/FeedbackLog/components/CollapsibleRow.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Flow/FeedbackLog/components/CollapsibleRow.tsx index 0bfc852501..818eb397f3 100644 --- a/editor.planx.uk/src/pages/FlowEditor/components/Flow/FeedbackLog/components/CollapsibleRow.tsx +++ b/editor.planx.uk/src/pages/FlowEditor/components/Flow/FeedbackLog/components/CollapsibleRow.tsx @@ -126,6 +126,17 @@ export const CollapsibleRow: React.FC = (item) => { userContext: "What were you doing?", }; + enum EmojiRating { + Terrible, + Poor, + Average, + Good, + Excellent, + } + + const feedbackScore = + item.feedbackScore && EmojiRating[item.feedbackScore + 1]; // enums are 0-indexed + const renderContent = (key: string, value: any) => { if (key === "combinedHelp" && value) { return ; @@ -144,6 +155,7 @@ export const CollapsibleRow: React.FC = (item) => { {format(new Date(item.createdAt), "dd/MM/yy hh:mm:ss")} + {feedbackScore} {commentSummary} ({ query: gql` - query GetFeebackForFlow($teamSlug: String!, $flowSlug: String!) { + query GetFeedbackForFlow($teamSlug: String!, $flowSlug: String!) { feedback: feedback_summary( order_by: { created_at: asc } where: { @@ -57,6 +58,7 @@ const feedbackRoutes = compose( nodeType: node_type userComment: user_comment userContext: user_context + feedbackScore: feedback_score createdAt: created_at address } @@ -70,7 +72,7 @@ const feedbackRoutes = compose( view: , }; }), - }), + }) ); export default feedbackRoutes; diff --git a/hasura.planx.uk/metadata/tables.yaml b/hasura.planx.uk/metadata/tables.yaml index f07ac12be2..e1e4632c98 100644 --- a/hasura.planx.uk/metadata/tables.yaml +++ b/hasura.planx.uk/metadata/tables.yaml @@ -305,15 +305,17 @@ - role: demoUser permission: columns: - - feedback_id - - device - - node_data - address + - created_at + - device + - feedback_id + - feedback_score - feedback_type - help_definition - help_sources - help_text - intersecting_constraints + - node_data - node_id - node_text - node_title @@ -325,7 +327,6 @@ - uprn - user_comment - user_context - - created_at filter: team: flows: @@ -335,15 +336,17 @@ - role: platformAdmin permission: columns: - - feedback_id - - device - - node_data - address + - created_at + - device + - feedback_id + - feedback_score - feedback_type - help_definition - help_sources - help_text - intersecting_constraints + - node_data - node_id - node_text - node_title @@ -355,21 +358,22 @@ - uprn - user_comment - user_context - - created_at filter: {} comment: "" - role: teamEditor permission: columns: - - feedback_id - - device - - node_data - address + - created_at + - device + - feedback_id + - feedback_score - feedback_type - help_definition - help_sources - help_text - intersecting_constraints + - node_data - node_id - node_text - node_title @@ -381,7 +385,6 @@ - uprn - user_comment - user_context - - created_at filter: team: members: diff --git a/hasura.planx.uk/migrations/1731495319577_run_sql_migration/down.sql b/hasura.planx.uk/migrations/1731495319577_run_sql_migration/down.sql new file mode 100644 index 0000000000..2f5d155ea4 --- /dev/null +++ b/hasura.planx.uk/migrations/1731495319577_run_sql_migration/down.sql @@ -0,0 +1,29 @@ +-- Drop the existing feedback_summary view +DROP VIEW IF EXISTS public.feedback_summary; + +-- Recreate the feedback_summary view without feedback_score +CREATE OR REPLACE VIEW "public"."feedback_summary" AS + SELECT fb.id AS feedback_id, + t.slug AS team_slug, + f.slug AS service_slug, + fb.created_at, + fb.node_id, + fb.device, + fb.user_context, + fb.user_comment, + fb.feedback_type, + fb.status, + fb.node_type, + fb.node_data, + COALESCE((fb.node_data ->> 'title'::text), (fb.node_data ->> 'text'::text), (fb.node_data ->> 'flagSet'::text)) AS node_title, + (fb.node_data ->> 'description'::text) AS node_text, + (fb.node_data ->> 'info'::text) AS help_text, + (fb.node_data ->> 'policyRef'::text) AS help_sources, + (fb.node_data ->> 'howMeasured'::text) AS help_definition, + COALESCE(((((fb.user_data -> 'passport'::text) -> 'data'::text) -> '_address'::text) ->> 'single_line_address'::text), ((((fb.user_data -> 'passport'::text) -> 'data'::text) -> '_address'::text) ->> 'title'::text)) AS address, + ((((fb.user_data -> 'passport'::text) -> 'data'::text) -> '_address'::text) ->> 'uprn'::text) AS uprn, + (((fb.user_data -> 'passport'::text) -> 'data'::text) ->> 'proposal.projectType'::text) AS project_type, + (((fb.user_data -> 'passport'::text) -> 'data'::text) ->> 'property.constraints.planning'::text) AS intersecting_constraints + FROM ((feedback fb + LEFT JOIN flows f ON ((f.id = fb.flow_id))) + LEFT JOIN teams t ON ((t.id = fb.team_id))); \ No newline at end of file diff --git a/hasura.planx.uk/migrations/1731495319577_run_sql_migration/up.sql b/hasura.planx.uk/migrations/1731495319577_run_sql_migration/up.sql new file mode 100644 index 0000000000..7aee6db1e7 --- /dev/null +++ b/hasura.planx.uk/migrations/1731495319577_run_sql_migration/up.sql @@ -0,0 +1,26 @@ +CREATE OR REPLACE VIEW "public"."feedback_summary" AS + SELECT fb.id AS feedback_id, + t.slug AS team_slug, + f.slug AS service_slug, + fb.created_at, + fb.node_id, + fb.device, + fb.user_context, + fb.user_comment, + fb.feedback_type, + fb.status, + fb.node_type, + fb.node_data, + COALESCE((fb.node_data ->> 'title'::text), (fb.node_data ->> 'text'::text), (fb.node_data ->> 'flagSet'::text)) AS node_title, + (fb.node_data ->> 'description'::text) AS node_text, + (fb.node_data ->> 'info'::text) AS help_text, + (fb.node_data ->> 'policyRef'::text) AS help_sources, + (fb.node_data ->> 'howMeasured'::text) AS help_definition, + COALESCE(((((fb.user_data -> 'passport'::text) -> 'data'::text) -> '_address'::text) ->> 'single_line_address'::text), ((((fb.user_data -> 'passport'::text) -> 'data'::text) -> '_address'::text) ->> 'title'::text)) AS address, + ((((fb.user_data -> 'passport'::text) -> 'data'::text) -> '_address'::text) ->> 'uprn'::text) AS uprn, + (((fb.user_data -> 'passport'::text) -> 'data'::text) ->> 'proposal.projectType'::text) AS project_type, + (((fb.user_data -> 'passport'::text) -> 'data'::text) ->> 'property.constraints.planning'::text) AS intersecting_constraints, + fb.feedback_score + FROM ((feedback fb + LEFT JOIN flows f ON ((f.id = fb.flow_id))) + LEFT JOIN teams t ON ((t.id = fb.team_id))); From 0fa25c10c2dcf1060c3c9cad895086a1a0af8d37 Mon Sep 17 00:00:00 2001 From: augustlindemer <118665588+augustlindemer@users.noreply.github.com> Date: Wed, 13 Nov 2024 15:05:55 +0000 Subject: [PATCH 3/6] fix: customise and move location of Notice component More Information button (#3947) --- .../src/@planx/components/Notice/Public.tsx | 69 ++++++++++++++++--- 1 file changed, 61 insertions(+), 8 deletions(-) diff --git a/editor.planx.uk/src/@planx/components/Notice/Public.tsx b/editor.planx.uk/src/@planx/components/Notice/Public.tsx index 2f5a875314..1a2e122008 100644 --- a/editor.planx.uk/src/@planx/components/Notice/Public.tsx +++ b/editor.planx.uk/src/@planx/components/Notice/Public.tsx @@ -1,5 +1,6 @@ import { mostReadable } from "@ctrl/tinycolor"; import ErrorOutline from "@mui/icons-material/ErrorOutline"; +import HelpIcon from "@mui/icons-material/Help"; import Box, { BoxProps } from "@mui/material/Box"; import Button from "@mui/material/Button"; import { styled, useTheme } from "@mui/material/styles"; @@ -8,14 +9,18 @@ import type { Notice } from "@planx/components/Notice/model"; import Card, { contentFlowSpacing, } from "@planx/components/shared/Preview/Card"; -import { CardHeader } from "@planx/components/shared/Preview/CardHeader/CardHeader"; import { PublicProps } from "@planx/components/shared/types"; import { useAnalyticsTracking } from "pages/FlowEditor/lib/analytics/provider"; import React from "react"; import { getContrastTextColor } from "styleUtils"; import { FONT_WEIGHT_SEMI_BOLD } from "theme"; +import { emptyContent } from "ui/editor/RichTextInput/RichTextInput"; import ReactMarkdownOrHtml from "ui/shared/ReactMarkdownOrHtml/ReactMarkdownOrHtml"; +import { HelpButton, Image } from "../shared/Preview/CardHeader/styled"; +import MoreInfo from "../shared/Preview/MoreInfo"; +import MoreInfoSection from "../shared/Preview/MoreInfoSection"; + export type Props = PublicProps; const Container = styled(Box, { @@ -77,12 +82,21 @@ const NoticeComponent: React.FC = (props) => { props.color, theme.palette.text.primary, ); + + const { info, policyRef, howMeasured, definitionImg } = props; + + const [open, setOpen] = React.useState(false); + const { trackEvent } = useAnalyticsTracking(); + + const handleHelpClick = () => { + setOpen(true); + trackEvent({ event: "helpClick", metadata: {} }); // This returns a promise but we don't need to await for it + }; + const handleSubmit = !props.resetButton ? () => props.handleSubmit?.() : undefined; - const { trackEvent } = useAnalyticsTracking(); - const handleNoticeResetClick = () => { trackEvent({ event: "flowDirectionChange", @@ -95,11 +109,6 @@ const NoticeComponent: React.FC = (props) => { return ( <> - @@ -117,6 +126,50 @@ const NoticeComponent: React.FC = (props) => { + {!!(info || policyRef || howMeasured) && ( + + + More information + + + )} + setOpen(false)}> + {info && info !== emptyContent ? ( + + + + ) : undefined} + {policyRef && policyRef !== emptyContent ? ( + + + + ) : undefined} + {howMeasured && howMeasured !== emptyContent ? ( + + <> + {definitionImg && ( + + )} + + + + ) : undefined} + {props.resetButton && (