From 94391e5c61f973e1c1d5bd05b2a1908be97dc381 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Fri, 13 Sep 2024 14:32:49 +0100 Subject: [PATCH] refactor: Split up Sidebar child components (#3668) --- .../components/Sidebar/DebugConsole.tsx | 51 ++++ .../Sidebar/{ => Publish}/PublishDialog.tsx | 2 +- .../Sidebar/Publish/PublishFlowButton.tsx | 192 +++++++++++++ .../FlowEditor/components/Sidebar/index.tsx | 259 ++---------------- .../src/pages/FlowEditor/index.tsx | 7 +- 5 files changed, 263 insertions(+), 248 deletions(-) create mode 100644 editor.planx.uk/src/pages/FlowEditor/components/Sidebar/DebugConsole.tsx rename editor.planx.uk/src/pages/FlowEditor/components/Sidebar/{ => Publish}/PublishDialog.tsx (99%) create mode 100644 editor.planx.uk/src/pages/FlowEditor/components/Sidebar/Publish/PublishFlowButton.tsx diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Sidebar/DebugConsole.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Sidebar/DebugConsole.tsx new file mode 100644 index 0000000000..7e9448e1e6 --- /dev/null +++ b/editor.planx.uk/src/pages/FlowEditor/components/Sidebar/DebugConsole.tsx @@ -0,0 +1,51 @@ +import ReactJson from "@microlink/react-json-view"; +import Box from "@mui/material/Box"; +import { styled } from "@mui/material/styles"; +import Typography from "@mui/material/Typography"; +import React from "react"; + +import { useStore } from "../../lib/store"; + +const Console = styled(Box)(({ theme }) => ({ + overflow: "auto", + padding: theme.spacing(2), + height: "100%", + backgroundColor: theme.palette.background.dark, + color: theme.palette.common.white, +})); + +export const DebugConsole = () => { + const [passport, breadcrumbs, flowId, cachedBreadcrumbs] = useStore( + (state) => [ + state.computePassport(), + state.breadcrumbs, + state.id, + state.cachedBreadcrumbs, + ], + ); + return ( + +
+ + + + Download the flow schema + + +
+
+ ); +}; diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Sidebar/PublishDialog.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Sidebar/Publish/PublishDialog.tsx similarity index 99% rename from editor.planx.uk/src/pages/FlowEditor/components/Sidebar/PublishDialog.tsx rename to editor.planx.uk/src/pages/FlowEditor/components/Sidebar/Publish/PublishDialog.tsx index 5aeef4e9cf..35495d1c56 100644 --- a/editor.planx.uk/src/pages/FlowEditor/components/Sidebar/PublishDialog.tsx +++ b/editor.planx.uk/src/pages/FlowEditor/components/Sidebar/Publish/PublishDialog.tsx @@ -25,7 +25,7 @@ import React, { useState } from "react"; import { useAsync } from "react-use"; import Caret from "ui/icons/Caret"; -import { useStore } from "../../lib/store"; +import { useStore } from "../../../lib/store"; export interface AlteredNode { id: string; diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Sidebar/Publish/PublishFlowButton.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Sidebar/Publish/PublishFlowButton.tsx new file mode 100644 index 0000000000..418f21e3d8 --- /dev/null +++ b/editor.planx.uk/src/pages/FlowEditor/components/Sidebar/Publish/PublishFlowButton.tsx @@ -0,0 +1,192 @@ +import Badge from "@mui/material/Badge"; +import Box from "@mui/material/Box"; +import Button from "@mui/material/Button"; +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 Link from "@mui/material/Link"; +import Typography from "@mui/material/Typography"; +import { AxiosError } from "axios"; +import { useStore } from "pages/FlowEditor/lib/store"; +import { formatLastPublishMessage } from "pages/FlowEditor/utils"; +import React, { useState } from "react"; +import { useAsync } from "react-use"; +import Input from "ui/shared/Input"; + +import { urls } from ".."; +import { + AlteredNode, + AlteredNodesSummaryContent, + ValidationCheck, + ValidationChecks, +} from "./PublishDialog"; + +export const PublishFlowButton = () => { + const [ + flowId, + publishFlow, + lastPublished, + lastPublisher, + validateAndDiffFlow, + ] = useStore((state) => [ + state.id, + state.publishFlow, + state.lastPublished, + state.lastPublisher, + state.validateAndDiffFlow, + ]); + + const [lastPublishedTitle, setLastPublishedTitle] = useState( + "This flow is not published yet", + ); + const [validationChecks, setValidationChecks] = useState( + [], + ); + const [alteredNodes, setAlteredNodes] = useState(); + const [dialogOpen, setDialogOpen] = useState(false); + const [summary, setSummary] = useState(); + + const handleCheckForChangesToPublish = async () => { + try { + setLastPublishedTitle("Checking for changes..."); + const alteredFlow = await validateAndDiffFlow(flowId); + setAlteredNodes( + alteredFlow?.data.alteredNodes ? alteredFlow.data.alteredNodes : [], + ); + setLastPublishedTitle( + alteredFlow?.data.alteredNodes + ? `Found changes to ${alteredFlow.data.alteredNodes.length} nodes` + : alteredFlow?.data.message, + ); + setValidationChecks(alteredFlow?.data?.validationChecks); + setDialogOpen(true); + } catch (error) { + setLastPublishedTitle("Error checking for changes to publish"); + + if (error instanceof AxiosError) { + alert(error.response?.data?.error); + } else { + alert( + `Error checking for changes to publish. Confirm that your graph does not have any corrupted nodes and that all external portals are valid. \n${error}`, + ); + } + } + }; + + const handlePublish = async () => { + try { + setDialogOpen(false); + setLastPublishedTitle("Publishing changes..."); + const { alteredNodes, message } = await publishFlow(flowId, summary); + setLastPublishedTitle( + alteredNodes + ? `Successfully published changes to ${alteredNodes.length} nodes` + : `${message}` || "No new changes to publish", + ); + } catch (error) { + setLastPublishedTitle("Error trying to publish"); + alert(error); + } + }; + + const _lastPublishedRequest = useAsync(async () => { + const date = await lastPublished(flowId); + const user = await lastPublisher(flowId); + + setLastPublishedTitle(formatLastPublishMessage(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]; + + return ( + + + + + + setDialogOpen(false)} + aria-labelledby="alert-dialog-title" + aria-describedby="alert-dialog-description" + maxWidth="md" + > + + {`Check for changes to publish`} + + + {alteredNodes?.length ? ( + <> + + + + + {`Preview these content changes in-service before publishing `} + + {`here (opens in a new tab).`} + + + + setSummary(e.target.value)} + /> + + ) : ( + + {`No new changes to publish`} + + )} + + + + + + + + {lastPublishedTitle} + + + + ); +}; diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Sidebar/index.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Sidebar/index.tsx index b76df2413f..65434a0e0c 100644 --- a/editor.planx.uk/src/pages/FlowEditor/components/Sidebar/index.tsx +++ b/editor.planx.uk/src/pages/FlowEditor/components/Sidebar/index.tsx @@ -1,50 +1,36 @@ -import ReactJson from "@microlink/react-json-view"; import LanguageIcon from "@mui/icons-material/Language"; import OpenInNewIcon from "@mui/icons-material/OpenInNew"; import OpenInNewOffIcon from "@mui/icons-material/OpenInNewOff"; -import Badge from "@mui/material/Badge"; import Box from "@mui/material/Box"; import Button from "@mui/material/Button"; import Container from "@mui/material/Container"; -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 Link from "@mui/material/Link"; import { styled } from "@mui/material/styles"; import Tabs from "@mui/material/Tabs"; import Tooltip from "@mui/material/Tooltip"; -import Typography from "@mui/material/Typography"; -import { AxiosError } from "axios"; import { hasFeatureFlag } from "lib/featureFlags"; -import { formatLastPublishMessage } from "pages/FlowEditor/utils"; import React, { useState } from "react"; -import { useAsync } from "react-use"; +import { rootFlowPath } from "routes/utils"; import Permission from "ui/editor/Permission"; import Reset from "ui/icons/Reset"; -import Input from "ui/shared/Input"; import Questions from "../../../Preview/Questions"; import { useStore } from "../../lib/store"; +import { DebugConsole } from "./DebugConsole"; import EditHistory from "./EditHistory"; -import { - AlteredNode, - AlteredNodesSummaryContent, - ValidationCheck, - ValidationChecks, -} from "./PublishDialog"; +import { PublishFlowButton } from "./Publish/PublishFlowButton"; import Search from "./Search"; import StyledTab from "./StyledTab"; type SidebarTabs = "PreviewBrowser" | "History" | "Search" | "Console"; -const Console = styled(Box)(({ theme }) => ({ - overflow: "auto", - padding: theme.spacing(2), - height: "100%", - backgroundColor: theme.palette.background.dark, - color: theme.palette.common.white, -})); +const baseUrl = `${window.location.origin}${rootFlowPath(false)}`; + +export const urls = { + preview: baseUrl + "/preview", + draft: baseUrl + "/draft", + analytics: baseUrl + "/published" + "?analytics=false", +}; const Root = styled(Box)(({ theme }) => ({ position: "relative", @@ -117,152 +103,28 @@ const TabList = styled(Box)(({ theme }) => ({ }, })); -const DebugConsole = () => { - const [passport, breadcrumbs, flowId, cachedBreadcrumbs] = useStore( - (state) => [ - state.computePassport(), - state.breadcrumbs, - state.id, - state.cachedBreadcrumbs, - ], - ); - return ( - - - - ); -}; - -const Sidebar: React.FC<{ - url: string; -}> = React.memo((props) => { - const [showDebugConsole, setDebugConsoleVisibility] = useState(false); - const [ - flowId, - resetPreview, - publishFlow, - lastPublished, - lastPublisher, - validateAndDiffFlow, - isFlowPublished, - ] = useStore((state) => [ - state.id, +const Sidebar: React.FC = React.memo(() => { + const [resetPreview, isFlowPublished] = useStore((state) => [ state.resetPreview, - state.publishFlow, - state.lastPublished, - state.lastPublisher, - state.validateAndDiffFlow, state.isFlowPublished, ]); - const [lastPublishedTitle, setLastPublishedTitle] = useState( - "This flow is not published yet", - ); - const [validationChecks, setValidationChecks] = useState( - [], - ); - const [alteredNodes, setAlteredNodes] = useState(); - const [dialogOpen, setDialogOpen] = useState(false); - const [summary, setSummary] = useState(); + const [activeTab, setActiveTab] = useState("PreviewBrowser"); const handleChange = (event: React.SyntheticEvent, newValue: SidebarTabs) => { setActiveTab(newValue); }; - const handleCheckForChangesToPublish = async () => { - try { - setLastPublishedTitle("Checking for changes..."); - const alteredFlow = await validateAndDiffFlow(flowId); - setAlteredNodes( - alteredFlow?.data.alteredNodes ? alteredFlow.data.alteredNodes : [], - ); - setLastPublishedTitle( - alteredFlow?.data.alteredNodes - ? `Found changes to ${alteredFlow.data.alteredNodes.length} nodes` - : alteredFlow?.data.message, - ); - setValidationChecks(alteredFlow?.data?.validationChecks); - setDialogOpen(true); - } catch (error) { - setLastPublishedTitle("Error checking for changes to publish"); - - if (error instanceof AxiosError) { - alert(error.response?.data?.error); - } else { - alert( - `Error checking for changes to publish. Confirm that your graph does not have any corrupted nodes and that all external portals are valid. \n${error}`, - ); - } - } - }; - - const handlePublish = async () => { - try { - setDialogOpen(false); - setLastPublishedTitle("Publishing changes..."); - const { alteredNodes, message } = await publishFlow(flowId, summary); - setLastPublishedTitle( - alteredNodes - ? `Successfully published changes to ${alteredNodes.length} nodes` - : `${message}` || "No new changes to publish", - ); - } catch (error) { - setLastPublishedTitle("Error trying to publish"); - alert(error); - } - }; - - const _lastPublishedRequest = useAsync(async () => { - const date = await lastPublished(flowId); - const user = await lastPublisher(flowId); - - setLastPublishedTitle(formatLastPublishMessage(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]; - return (
- + )} - - - - - - setDialogOpen(false)} - aria-labelledby="alert-dialog-title" - aria-describedby="alert-dialog-description" - maxWidth="md" - > - - {`Check for changes to publish`} - - - {alteredNodes?.length ? ( - <> - - - - - {`Preview these content changes in-service before publishing `} - - {`here (opens in a new tab).`} - - - - setSummary(e.target.value)} - /> - - ) : ( - - {`No new changes to publish`} - - )} - - - - - - - - {lastPublishedTitle} - - - +
diff --git a/editor.planx.uk/src/pages/FlowEditor/index.tsx b/editor.planx.uk/src/pages/FlowEditor/index.tsx index 1e68d998e7..cf55d4806b 100644 --- a/editor.planx.uk/src/pages/FlowEditor/index.tsx +++ b/editor.planx.uk/src/pages/FlowEditor/index.tsx @@ -5,7 +5,6 @@ import { styled } from "@mui/material/styles"; import { HEADER_HEIGHT_EDITOR } from "components/Header"; import React, { useRef } from "react"; -import { rootFlowPath } from "../../routes/utils"; import Flow from "./components/Flow"; import Sidebar from "./components/Sidebar"; import { useStore } from "./lib/store"; @@ -38,11 +37,7 @@ const FlowEditor: React.FC = ({ flow, breadcrumbs }) => { - {showSidebar && ( - - )} + {showSidebar && } ); };