diff --git a/api.planx.uk/helpers.ts b/api.planx.uk/helpers.ts index 3122695d5c..cc30192411 100644 --- a/api.planx.uk/helpers.ts +++ b/api.planx.uk/helpers.ts @@ -169,7 +169,7 @@ const dataMerged = async ( const { slug, team, publishedFlows } = response; let { data } = response; - // only flatten external portals that are published, unless we're loading draftDataOnly on an /unpublished route + // only flatten external portals that are published, unless we're loading draftDataOnly if (isPortal && !draftDataOnly) { if (publishedFlows?.[0]?.data) { data = publishedFlows[0].data; @@ -192,7 +192,7 @@ const dataMerged = async ( ob[id] = { ...node, type: ComponentType.InternalPortal, - data: { text: slug }, + data: { text: `${team.slug}/${slug}` }, }; } @@ -203,7 +203,7 @@ const dataMerged = async ( edges: [node.data?.flowId], }; - // Recursively merge flow + // recursively merge flow if (!isMerged) { await dataMerged(node.data?.flowId, ob, true, draftDataOnly); } diff --git a/api.planx.uk/tests/mocks/validateAndPublishMocks.ts b/api.planx.uk/tests/mocks/validateAndPublishMocks.ts index a8bf58c1ec..d3db8718cf 100644 --- a/api.planx.uk/tests/mocks/validateAndPublishMocks.ts +++ b/api.planx.uk/tests/mocks/validateAndPublishMocks.ts @@ -162,7 +162,7 @@ export const flattenedParentFlow: FlowGraph = { edges: ["QuestionInsidePortal"], type: 300, data: { - text: "child-flow", + text: "testing/child-flow", }, }, OptionB: { diff --git a/editor.planx.uk/src/pages/FlowEditor/components/PreviewBrowser.tsx b/editor.planx.uk/src/pages/FlowEditor/components/PreviewBrowser.tsx index 570a42d3ff..98296287f1 100644 --- a/editor.planx.uk/src/pages/FlowEditor/components/PreviewBrowser.tsx +++ b/editor.planx.uk/src/pages/FlowEditor/components/PreviewBrowser.tsx @@ -4,12 +4,15 @@ import OpenInNewIcon from "@mui/icons-material/OpenInNew"; import OpenInNewOffIcon from "@mui/icons-material/OpenInNewOff"; import RefreshIcon from "@mui/icons-material/Refresh"; import SignalCellularAltIcon from "@mui/icons-material/SignalCellularAlt"; +import Badge from "@mui/material/Badge"; import Box from "@mui/material/Box"; import Button from "@mui/material/Button"; +import Collapse from "@mui/material/Collapse"; import Dialog from "@mui/material/Dialog"; import DialogActions from "@mui/material/DialogActions"; import DialogContent from "@mui/material/DialogContent"; import DialogTitle from "@mui/material/DialogTitle"; +import Divider from "@mui/material/Divider"; import Link from "@mui/material/Link"; import { styled } from "@mui/material/styles"; import Tooltip from "@mui/material/Tooltip"; @@ -19,6 +22,7 @@ import { AxiosError } from "axios"; import formatDistanceToNow from "date-fns/formatDistanceToNow"; import React, { useState } from "react"; import { useAsync } from "react-use"; +import Caret from "ui/icons/Caret"; import Input from "ui/shared/Input"; import Questions from "../../Preview/Questions"; @@ -68,7 +72,7 @@ const DebugConsole = () => { ); }; -function PublishChangeItem(props: any) { +function AlteredNodeItem(props: any) { const { node } = props; let text, data; @@ -93,6 +97,137 @@ function PublishChangeItem(props: any) { ); } +interface AlteredNodesSummary { + title: string; + portals: string[]; + updated: number; + deleted: number; +} + +function AlteredNodesSummaryContent(props: any) { + const { alteredNodes, url } = props; + const [expandNodes, setExpandNodes] = useState(false); + + const changeSummary: AlteredNodesSummary = { + title: "", + portals: [], + updated: 0, + deleted: 0, + }; + + alteredNodes.map((node: any) => { + if (node.id === "0") { + changeSummary["title"] = + "You are publishing the main service for the first time."; + } else if (node.id && Object.keys(node).length === 1) { + changeSummary["deleted"] += 1; + } else if (node.type === TYPES.InternalPortal) { + if (node.data?.text?.includes("/")) { + changeSummary["portals"].push(node.data?.text); + } + } else if (node.type) { + changeSummary["updated"] += 1; + } + + return changeSummary; + }); + + return ( + + {changeSummary["title"] && ( + + {changeSummary["title"]} + + )} + {(changeSummary["updated"] > 0 || changeSummary["deleted"] > 0) && ( + +
    +
  • + {`${changeSummary["updated"]} nodes have been updated or added`} +
  • +
  • + {`${changeSummary["deleted"]} nodes have been deleted`} +
  • +
+ + + {`See detailed changelog `} + + + + +
    + {alteredNodes.map((node: any) => ( +
  • + +
  • + ))} +
+
+
+
+
+ )} + + {changeSummary["portals"].length > 0 && ( + + {`This includes recently published changes in the following nested services:`} +
    + {changeSummary["portals"].map((portal, i) => ( +
  • + {useStore.getState().canUserEditTeam(portal.split("/")[0]) ? ( + + {portal} + + ) : ( + {portal} + )} +
  • + ))} +
+
+ )} + + + + {`Review these content changes in-service before publishing `} + + {`here`} + + {` (opens in a new tab).`} + + +
+ ); +} + const PreviewBrowser: React.FC<{ url: string; }> = React.memo((props) => { @@ -106,6 +241,7 @@ const PreviewBrowser: React.FC<{ lastPublisher, validateAndDiffFlow, isFlowPublished, + isPlatformAdmin, ] = useStore((state) => [ state.id, state.flowAnalyticsLink, @@ -115,6 +251,7 @@ const PreviewBrowser: React.FC<{ state.lastPublisher, state.validateAndDiffFlow, state.isFlowPublished, + state.user?.isPlatformAdmin, ]); const [key, setKey] = useState(false); const [lastPublishedTitle, setLastPublishedTitle] = useState( @@ -135,6 +272,13 @@ const PreviewBrowser: React.FC<{ setLastPublishedTitle(formatLastPublish(date, user)); }); + const _validateAndDiffRequest = useAsync(async () => { + const newChanges = await validateAndDiffFlow(flowId); + setAlteredNodes( + newChanges?.data.alteredNodes ? newChanges.data.alteredNodes : [], + ); + }); + // useStore.getState().getTeam().slug undefined here, use window instead const teamSlug = window.location.pathname.split("/")[1]; @@ -145,8 +289,9 @@ const PreviewBrowser: React.FC<{ + { @@ -183,16 +328,19 @@ const PreviewBrowser: React.FC<{ )} - - - - - + {isPlatformAdmin && ( + + + + + + )} + + {isFlowPublished ? ( - - + }} + > + CHECK FOR CHANGES TO PUBLISH + + setDialogOpen(false)} @@ -276,15 +437,10 @@ const PreviewBrowser: React.FC<{ {alteredNodes?.length ? ( <> - -
    - {alteredNodes.map((a: any) => ( -
  • - -
  • - ))} -
-
+