Skip to content

Commit

Permalink
♻️ [open-formulieren/open-forms#4929] Simplify props for payment/conf…
Browse files Browse the repository at this point in the history
…irmation status view page
  • Loading branch information
sergei-maertens committed Jan 17, 2025
1 parent 703519a commit 8bb788d
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 92 deletions.
52 changes: 3 additions & 49 deletions src/components/Form.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import {useIntl} from 'react-intl';
import {
Navigate,
Outlet,
Route,
Routes,
useLocation,
useMatch,
useNavigate,
Expand All @@ -16,11 +14,8 @@ import {useImmerReducer} from 'use-immer';

import {AnalyticsToolsConfigContext, ConfigContext} from 'Context';
import {destroy, get} from 'api';
import ErrorBoundary from 'components/Errors/ErrorBoundary';
import Loader from 'components/Loader';
import {ConfirmationView, StartPaymentView} from 'components/PostCompletionViews';
import ProgressIndicator from 'components/ProgressIndicator';
import RequireSubmission from 'components/RequireSubmission';
import {
PI_TITLE,
START_FORM_QUERY_PARAM,
Expand Down Expand Up @@ -49,12 +44,6 @@ const reducer = (draft, action) => {
draft.submission = action.payload;
break;
}
case 'PROCESSING_FAILED': {
// put the submission back in the state so we can re-submit
const {submission} = action.payload;
draft.submission = submission;
break;
}
case 'DESTROY_SUBMISSION': {
return {
...initialState,
Expand Down Expand Up @@ -158,11 +147,6 @@ const Form = () => {
navigate('/');
};

const onProcessingFailure = (submission, errorMessage) => {
dispatch({type: 'PROCESSING_FAILED', payload: {submission}});
navigate('/overzicht', {state: {errorMessage}});
};

// handle redirect from payment provider to render appropriate page and include the
// params as state for the next component.
if (params.get('of_payment_status')) {
Expand All @@ -184,13 +168,15 @@ const Form = () => {
return <Loader modifiers={['centered']} />;
}

const submissionFromRouterState = routerState?.submission ?? null;
const submission = state.submission || submissionFromRouterState;

// Progress Indicator

const isIntroductionPage = !!introductionMatch;
const isStartPage = !isIntroductionPage && !summaryMatch && stepMatch == null && !paymentMatch;
const submissionAllowedSpec = state.submission?.submissionAllowed ?? form.submissionAllowed;
const showOverview = submissionAllowedSpec !== SUBMISSION_ALLOWED.noWithoutOverview;
const submission = state.submission || (!!paymentMatch && routerState.submission) || null;
const formName = form.name;
const needsPayment = submission ? submission.payment.isRequired : form.paymentRequired;

Expand Down Expand Up @@ -262,37 +248,6 @@ const Form = () => {
/>
) : null;

// Route the correct page based on URL
const router = (
<Routes>
<Route
path="betalen"
element={
<ErrorBoundary useCard>
<RequireSubmission
retrieveSubmissionFromContext
onFailure={onProcessingFailure}
component={StartPaymentView}
donwloadPDFText={form.submissionReportDownloadLinkTitle}
/>
</ErrorBoundary>
}
/>

<Route
path="bevestiging"
element={
<ErrorBoundary useCard>
<ConfirmationView
onFailure={onProcessingFailure}
downloadPDFText={form.submissionReportDownloadLinkTitle}
/>
</ErrorBoundary>
}
/>
</Routes>
);

// render the form step if there's an active submission (and no summary)
return (
<FormDisplay progressIndicator={progressIndicator}>
Expand All @@ -304,7 +259,6 @@ const Form = () => {
removeSubmissionId={removeSubmissionId}
>
<Outlet />
{router}
</SubmissionProvider>
</AnalyticsToolsConfigContext.Provider>
</FormDisplay>
Expand Down
46 changes: 30 additions & 16 deletions src/components/PostCompletionViews/ConfirmationView.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import PropTypes from 'prop-types';
import React, {useContext} from 'react';
import {FormattedMessage, defineMessage, useIntl} from 'react-intl';
import {useLocation, useSearchParams} from 'react-router-dom';
import {useLocation, useNavigate, useSearchParams} from 'react-router-dom';

import Body from 'components/Body';
import ErrorMessage from 'components/Errors/ErrorMessage';
import {GovMetricSnippet} from 'components/analytics';
import useFormContext from 'hooks/useFormContext';
import {DEBUG} from 'utils';

import PostCompletionView from './PostCompletionView';
Expand Down Expand Up @@ -106,41 +107,54 @@ ConfirmationViewDisplay.propTypes = {
downloadPDFText: PropTypes.node,
};

const ConfirmationView = ({onFailure, onConfirmed, downloadPDFText}) => {
const ConfirmationView = ({returnTo, onFailure, onConfirmed}) => {
const form = useFormContext();
// TODO: take statusUrl from session storage instead of router state / query params,
// which is the best tradeoff between security and convenience (state doesn't survive
// hard refreshes, query params is prone to accidental information leaking)
const location = useLocation();
const navigate = useNavigate();
const [params] = useSearchParams();
const statusUrl = params.get('statusUrl') ?? location.state?.statusUrl;
const submittedSubmission = location.state?.submission;

if (DEBUG) {
if (!statusUrl) {
throw new Error(
'You must pass the status URL via the router state (preferably) or query params.'
);
}
if (!submittedSubmission) {
throw new Error('You must pass the submitted submission via the router state.');
}

if (DEBUG && !statusUrl) {
throw new Error(
'You must pass the status URL via the router state (preferably) or query params.'
);
}

return (
<StatusUrlPoller
statusUrl={statusUrl}
onFailure={error => onFailure(submittedSubmission, error)}
onFailure={error => {
onFailure?.(error);
if (returnTo) {
const newState = {...(location.state || {}), errorMessage: error};
navigate(returnTo, {state: newState});
}
}}
onConfirmed={onConfirmed}
>
<ConfirmationViewDisplay downloadPDFText={downloadPDFText} />
<ConfirmationViewDisplay downloadPDFText={form.submissionReportDownloadLinkTitle} />
</StatusUrlPoller>
);
};

ConfirmationView.propTypes = {
/**
* Location to navigate to on failure.
*/
returnTo: PropTypes.string,
/**
* Optional callback to invoke when processing failed.
* @deprecated
*/
onFailure: PropTypes.func,
/**
* Optional callback to invoke when processing was successful.
* @deprecated
*/
onConfirmed: PropTypes.func,
downloadPDFText: PropTypes.node,
};

export {ConfirmationViewDisplay};
Expand Down
38 changes: 26 additions & 12 deletions src/components/PostCompletionViews/StartPaymentView.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import PropTypes from 'prop-types';
import {useContext} from 'react';
import {FormattedMessage, useIntl} from 'react-intl';
import {useLocation} from 'react-router-dom';
import {useLocation, useNavigate} from 'react-router-dom';

import Body from 'components/Body';
import ErrorBoundary from 'components/Errors/ErrorBoundary';
import useFormContext from 'hooks/useFormContext';
import {DEBUG} from 'utils';

import PostCompletionView from './PostCompletionView';
Expand Down Expand Up @@ -60,24 +61,37 @@ StartPaymentViewDisplay.propTypes = {
downloadPDFText: PropTypes.node,
};

const StartPaymentView = ({onFailure, downloadPDFText}) => {
const {statusUrl, submission} = useLocation().state || {};
if (DEBUG) {
if (!statusUrl) throw new Error('You must pass the status URL via the route state.');
if (!submission) {
throw new Error('You must pass the submitted submission via the router state.');
}
}
const StartPaymentView = ({returnTo, onFailure}) => {
const form = useFormContext();
const navigate = useNavigate();
const {statusUrl} = useLocation().state || {};
if (DEBUG && !statusUrl) throw new Error('You must pass the status URL via the route state.');
return (
<StatusUrlPoller statusUrl={statusUrl} onFailure={error => onFailure(submission, error)}>
<StartPaymentViewDisplay downloadPDFText={downloadPDFText} />
<StatusUrlPoller
statusUrl={statusUrl}
onFailure={error => {
onFailure(error);
if (returnTo) {
const newState = {...(location.state || {}), errorMessage: error};
navigate(returnTo, {state: newState});
}
}}
>
<StartPaymentViewDisplay downloadPDFText={form.submissionReportDownloadLinkTitle} />
</StatusUrlPoller>
);
};

StartPaymentView.propTypes = {
/**
* Location to navigate to on failure.
*/
returnTo: PropTypes.string,
/**
* Optional callback to invoke when processing failed.
* @deprecated
*/
onFailure: PropTypes.func,
downloadPDFText: PropTypes.node,
};

export default StartPaymentView;
Expand Down
17 changes: 2 additions & 15 deletions src/components/appointments/CreateAppointment/Confirmation.jsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,16 @@
import {useNavigate, useSearchParams} from 'react-router-dom';
import {useSearchParams} from 'react-router-dom';

import {ConfirmationView} from 'components/PostCompletionViews';
import useFormContext from 'hooks/useFormContext';

import {useCreateAppointmentContext} from './CreateAppointmentState';

const Confirmation = () => {
const form = useFormContext();
const [params] = useSearchParams();
const navigate = useNavigate();
const {reset, setProcessingError} = useCreateAppointmentContext();
const statusUrl = params.get('statusUrl');
if (!statusUrl) throw new Error('Missing statusUrl param');

const onProcessingFailure = (submission, errorMessage) => {
setProcessingError(errorMessage);
navigate('../overzicht');
};

return (
<ConfirmationView
onFailure={onProcessingFailure}
onConfirmed={reset}
downloadPDFText={form.submissionReportDownloadLinkTitle}
/>
<ConfirmationView returnTo="../overzicht" onFailure={setProcessingError} onConfirmed={reset} />
);
};

Expand Down
19 changes: 19 additions & 0 deletions src/components/formRoutes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import FormLandingPage from 'components/FormLandingPage';
import FormStart from 'components/FormStart';
import FormStep from 'components/FormStep';
import IntroductionPage from 'components/IntroductionPage';
import {ConfirmationView, StartPaymentView} from 'components/PostCompletionViews';
import RequireSubmission from 'components/RequireSubmission';
import {SessionTrackerModal} from 'components/Sessions';
import {SubmissionSummary} from 'components/Summary';
Expand Down Expand Up @@ -48,6 +49,24 @@ const formRoutes = [
</ErrorBoundary>
),
},
{
path: 'betalen',
element: (
<ErrorBoundary useCard>
<RequireSubmission retrieveSubmissionFromContext>
<StartPaymentView returnTo="/overzicht" />
</RequireSubmission>
</ErrorBoundary>
),
},
{
path: 'bevestiging',
element: (
<ErrorBoundary useCard>
<ConfirmationView returnTo="/overzicht" />
</ErrorBoundary>
),
},
];

export default formRoutes;

0 comments on commit 8bb788d

Please sign in to comment.