Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 62 additions & 4 deletions frontend/app/routes/payment-confirmation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { validatePaymentParams } from '~/utils/validate.server'
export const meta: MetaFunction = () => {
return [
{ title: 'Grant Interaction' },
{ name: 'description', content: 'Interaction success' },
{ name: 'description', content: 'Grant interaction result' },
]
}

Expand All @@ -20,6 +20,19 @@ export async function loader({ request, context }: LoaderFunctionArgs) {
const url = new URL(request.url)
const params = Object.fromEntries([...url.searchParams])

// Check for interaction result parameters (e.g., result=grant_rejected)
const interactionResult = params.result
if (interactionResult && interactionResult !== 'grant_approved') {
return data({
success: false,
error:
interactionResult === 'grant_rejected'
? 'The grant request was declined.'
: `Interaction failed: ${interactionResult}`,
params,
})
}

const validation = validatePaymentParams(params)
if (!validation.success) {
return data({
Expand Down Expand Up @@ -58,19 +71,64 @@ export async function loader({ request, context }: LoaderFunctionArgs) {
}
}

export default function PaymentComplete() {
const { params } = useLoaderData<typeof loader>()
export default function GrantInteraction() {
const loaderData = useLoaderData<typeof loader>()
const hasPostedMessage = useRef(false)
const isSuccess = loaderData.success

useEffect(() => {
if (hasPostedMessage.current) return

hasPostedMessage.current = true
if (window.opener) {
window.opener.postMessage({ type: 'GRANT_INTERACTION', ...params }, '*')
window.opener.postMessage(
{ type: 'GRANT_INTERACTION', ...loaderData.params },
'*',
)
}
}, [])

if (!isSuccess) {
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50">
<div className="max-w-md w-full space-y-8 p-8">
<div className="text-center">
<div className="mx-auto flex items-center justify-center h-24 w-24 rounded-full bg-red-100 mb-6">
<svg
className="h-12 w-12 text-red-600"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</div>

<h2 className="text-3xl font-bold text-gray-900 mb-4">
Interaction Failed
</h2>

<p className="text-gray-600 mb-8">
{'error' in loaderData && loaderData.error
? loaderData.error
: 'Something went wrong. Please try again.'}
</p>

<p className="text-gray-400 text-sm">
You can close this window.
</p>
</div>
</div>
</div>
)
}

return (
<div className="min-h-screen flex items-center justify-center bg-gray-50">
<div className="max-w-md w-full space-y-8 p-8">
Expand Down