diff --git a/editor.planx.uk/src/@planx/components/Confirmation/Public.test.tsx b/editor.planx.uk/src/@planx/components/Confirmation/Public.test.tsx
index 5d98b00970..0fb6c1807d 100644
--- a/editor.planx.uk/src/@planx/components/Confirmation/Public.test.tsx
+++ b/editor.planx.uk/src/@planx/components/Confirmation/Public.test.tsx
@@ -1,3 +1,6 @@
+import { ComponentType as TYPES } from "@opensystemslab/planx-core/types";
+import { act, screen, waitFor } from "@testing-library/react";
+import { FullStore, useStore } from "pages/FlowEditor/lib/store";
import React from "react";
import { setup } from "testUtils";
import { vi } from "vitest";
@@ -5,6 +8,10 @@ import { axe } from "vitest-axe";
import ConfirmationComponent from "./Public";
+const { getState, setState } = useStore;
+
+let initialState: FullStore;
+
vi.mock("@opensystemslab/planx-core", () => {
return {
CoreDomainClient: vi.fn().mockImplementation(() => ({
@@ -33,3 +40,85 @@ it("should not have any accessibility violations", async () => {
const results = await axe(container);
expect(results).toHaveNoViolations();
});
+
+describe("Confirmation component", () => {
+ const handleSubmit = vi.fn();
+
+ beforeAll(() => (initialState = getState()));
+
+ afterEach(() => waitFor(() => setState(initialState)));
+
+ it("hides the 'Continue' button if it's the final card in the flow", () => {
+ act(() =>
+ setState({
+ flow: {
+ _root: { edges: ["Send", "Confirmation"] },
+ Send: { type: TYPES.Send },
+ Confirmation: { type: TYPES.Confirmation },
+ },
+ breadcrumbs: { Send: { auto: false } },
+ }),
+ );
+
+ expect(getState().upcomingCardIds()).toEqual(["Confirmation"]);
+ expect(getState().isFinalCard()).toEqual(true);
+
+ const { user } = setup(
+ ,
+ );
+
+ expect(screen.queryByText("Continue")).not.toBeInTheDocument();
+ });
+
+ it("shows the 'Continue' button if there are nodes following it", () => {
+ act(() =>
+ setState({
+ flow: {
+ _root: { edges: ["Send", "Confirmation", "Feedback", "Notice"] },
+ Send: { type: TYPES.Send },
+ Confirmation: { type: TYPES.Confirmation },
+ Feedback: { type: TYPES.Feedback },
+ Notice: { type: TYPES.Notice },
+ },
+ breadcrumbs: { Send: { auto: false } },
+ }),
+ );
+
+ expect(getState().upcomingCardIds()).toEqual([
+ "Confirmation",
+ "Feedback",
+ "Notice",
+ ]);
+ expect(getState().isFinalCard()).toEqual(false);
+
+ const { user } = setup(
+ ,
+ );
+
+ expect(screen.queryByText("Continue")).toBeInTheDocument();
+ });
+});
diff --git a/editor.planx.uk/src/@planx/components/Confirmation/Public.tsx b/editor.planx.uk/src/@planx/components/Confirmation/Public.tsx
index f6cd94fd2c..661d663f4f 100644
--- a/editor.planx.uk/src/@planx/components/Confirmation/Public.tsx
+++ b/editor.planx.uk/src/@planx/components/Confirmation/Public.tsx
@@ -85,6 +85,7 @@ interface PresentationalProps extends Props {
}
export function Presentational(props: PresentationalProps) {
+ const isFinalCard = useStore().isFinalCard();
return (
)}
-
+
{Object.entries(props.applicableDetails).map(([k, v], i) => (
diff --git a/editor.planx.uk/src/@planx/components/shared/Preview/Card.test.tsx b/editor.planx.uk/src/@planx/components/shared/Preview/Card.test.tsx
index 949d8abe02..b7c40da8e8 100644
--- a/editor.planx.uk/src/@planx/components/shared/Preview/Card.test.tsx
+++ b/editor.planx.uk/src/@planx/components/shared/Preview/Card.test.tsx
@@ -1,4 +1,5 @@
import Button from "@mui/material/Button";
+import { ComponentType as TYPES } from "@opensystemslab/planx-core/types";
import { act, screen, waitFor } from "@testing-library/react";
import { FullStore, useStore } from "pages/FlowEditor/lib/store";
import React from "react";
@@ -50,6 +51,29 @@ describe("Card component", () => {
expect(screen.queryByText(saveButtonText)).not.toBeInTheDocument();
});
+ it("hides the Save/Resume option if the user has already passed Send", () => {
+ act(() =>
+ setState({
+ path: ApplicationPath.SaveAndReturn,
+ flow: {
+ _root: { edges: ["Send", "Confirmation", "Feedback", "Notice"] },
+ Send: { type: TYPES.Send },
+ Confirmation: { type: TYPES.Confirmation },
+ Feedback: { type: TYPES.Feedback },
+ Notice: { type: TYPES.Notice },
+ },
+ breadcrumbs: { Send: { auto: false } },
+ }),
+ );
+ const children = Confirmation Page
;
+ setup();
+
+ expect(screen.queryByText("Confirmation Page")).toBeInTheDocument();
+ expect(screen.queryByText("Continue")).toBeInTheDocument();
+ expect(screen.queryByText(resumeButtonText)).not.toBeInTheDocument();
+ expect(screen.queryByText(saveButtonText)).not.toBeInTheDocument();
+ });
+
it("updates state to navigate to the 'Resume' page if the 'Resume' button is clicked", async () => {
act(() => setState({ path: ApplicationPath.SaveAndReturn }));
const children = ;
diff --git a/editor.planx.uk/src/@planx/components/shared/Preview/Card.tsx b/editor.planx.uk/src/@planx/components/shared/Preview/Card.tsx
index 72c8e261af..de4372a8b8 100644
--- a/editor.planx.uk/src/@planx/components/shared/Preview/Card.tsx
+++ b/editor.planx.uk/src/@planx/components/shared/Preview/Card.tsx
@@ -3,6 +3,7 @@ import Button from "@mui/material/Button";
import Container from "@mui/material/Container";
import Fade from "@mui/material/Fade";
import { styled, Theme, useTheme } from "@mui/material/styles";
+import { ComponentType as TYPES } from "@opensystemslab/planx-core/types";
import { useAnalyticsTracking } from "pages/FlowEditor/lib/analytics/provider";
import { useStore } from "pages/FlowEditor/lib/store";
import React, { useEffect } from "react";
@@ -48,12 +49,21 @@ const Card: React.FC = ({
...props
}) => {
const theme = useTheme();
- const [path, visibleNode] = useStore((state) => [
+ const [path, visibleNode, breadcrumbs, flow] = useStore((state) => [
state.path,
state.currentCard,
+ state.breadcrumbs,
+ state.flow,
]);
+
+ // Check if we have a Send node in our breadcrumbs
+ // This is a better/more immediate proxy for "submitted" in the frontend because actual send events that populate lowcal_sessions.submitted_at are queued via Hasura
+ const hasSent = Object.keys(breadcrumbs).some(
+ (breadcrumbNodeId: string) => flow[breadcrumbNodeId]?.type === TYPES.Send,
+ );
+
const showSaveResumeButton =
- path === ApplicationPath.SaveAndReturn && handleSubmit;
+ path === ApplicationPath.SaveAndReturn && handleSubmit && !hasSent;
const { track } = useAnalyticsTracking();
useEffect(() => {
diff --git a/editor.planx.uk/src/pages/FlowEditor/lib/store/preview.ts b/editor.planx.uk/src/pages/FlowEditor/lib/store/preview.ts
index ef3a4f164c..a0f502d524 100644
--- a/editor.planx.uk/src/pages/FlowEditor/lib/store/preview.ts
+++ b/editor.planx.uk/src/pages/FlowEditor/lib/store/preview.ts
@@ -670,9 +670,8 @@ export const previewStore: StateCreator<
},
isFinalCard: () => {
- // Temporarily always returns false until upcomingCardIds is optimised
- // OSL Slack explanation: https://bit.ly/3x38IRY
- return false;
+ const { upcomingCardIds } = get();
+ return upcomingCardIds().length === 1;
},
restore: false,
diff --git a/editor.planx.uk/src/pages/Preview/Node.tsx b/editor.planx.uk/src/pages/Preview/Node.tsx
index caf83b6c6b..c61cfdbe3b 100644
--- a/editor.planx.uk/src/pages/Preview/Node.tsx
+++ b/editor.planx.uk/src/pages/Preview/Node.tsx
@@ -75,16 +75,13 @@ interface Props {
}
const Node: React.FC = (props) => {
- const [childNodesOf, isFinalCard, resetPreview, cachedBreadcrumbs] = useStore(
- (state) => [
- state.childNodesOf,
- state.isFinalCard(),
- state.resetPreview,
- state.cachedBreadcrumbs,
- ],
- );
-
- const handleSubmit = isFinalCard ? undefined : props.handleSubmit;
+ const [childNodesOf, resetPreview, cachedBreadcrumbs] = useStore((state) => [
+ state.childNodesOf,
+ state.resetPreview,
+ state.cachedBreadcrumbs,
+ ]);
+
+ const handleSubmit = props.handleSubmit;
const nodeId = props.node.id;
const previouslySubmittedData =