Skip to content

Commit 9f56164

Browse files
fix(global): show CMS thank-you message after HubSpot submit instead of blank box
1 parent 8f76c64 commit 9f56164

7 files changed

Lines changed: 149 additions & 12 deletions

File tree

‎apps/strapi/src/api/hubspot-form/content-types/hubspot-form/schema.json‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,14 @@
2525
"placeholderHeight": {
2626
"type": "integer",
2727
"default": 300
28+
},
29+
"thankYouImage": {
30+
"type": "component",
31+
"component": "utilities.basic-image",
32+
"repeatable": false
33+
},
34+
"thankYouMessage": {
35+
"type": "richtext"
2836
}
2937
}
3038
}
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1+
import basicImagePopulate from "../utilities/basic-image"
2+
13
export default {
24
populate: {
3-
form: true,
5+
form: {
6+
populate: {
7+
thankYouImage: basicImagePopulate,
8+
},
9+
},
410
},
511
}

‎apps/strapi/types/generated/contentTypes.d.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1157,6 +1157,8 @@ export interface ApiHubspotFormHubspotForm extends Struct.CollectionTypeSchema {
11571157
Schema.Attribute.DefaultTo<300>
11581158
portalId: Schema.Attribute.String & Schema.Attribute.Required
11591159
publishedAt: Schema.Attribute.DateTime
1160+
thankYouImage: Schema.Attribute.Component<"utilities.basic-image", false>
1161+
thankYouMessage: Schema.Attribute.RichText
11601162
updatedAt: Schema.Attribute.DateTime
11611163
updatedBy: Schema.Attribute.Relation<"oneToOne", "admin::user"> &
11621164
Schema.Attribute.Private

‎apps/ui/src/components/page-builder/components/forms/strapi-hubspot-form-ssr/HubSpotSsrForm.tsx‎

Lines changed: 59 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { zodResolver } from "@hookform/resolvers/zod"
44
import { WarningIcon } from "@phosphor-icons/react/ssr"
55
import { useMutation } from "@tanstack/react-query"
66
import { useTranslations } from "next-intl"
7-
import { useMemo } from "react"
7+
import { useMemo, type ReactNode } from "react"
88
import { useForm } from "react-hook-form"
99

1010
import { SectionTitle } from "@/components/elementary/section-header"
@@ -32,6 +32,12 @@ interface HubSpotSsrFormProps {
3232
readonly onSubmitted?: (values: Record<string, unknown>) => void
3333
/** Optional heading rendered above the form. Omitted when absent. */
3434
readonly title?: string | null
35+
/**
36+
* Server-rendered content shown in place of the form after a successful
37+
* submission. Takes precedence over HubSpot's `inlineMessage`, which is
38+
* empty for forms built in the HubSpot new editor.
39+
*/
40+
readonly thankYouContent?: ReactNode
3541
}
3642

3743
interface SubmitPayload {
@@ -41,6 +47,18 @@ interface SubmitPayload {
4147

4248
interface SubmitResult {
4349
inlineMessage?: string
50+
redirectUri?: string
51+
}
52+
53+
/** Returns the trimmed string, or undefined when empty or not a string. */
54+
function nonEmpty(value: unknown): string | undefined {
55+
if (typeof value !== "string") {
56+
return undefined
57+
}
58+
59+
const trimmed = value.trim()
60+
61+
return trimmed.length > 0 ? trimmed : undefined
4462
}
4563

4664
async function submitHubSpotForm(
@@ -90,6 +108,7 @@ function HubSpotSsrFormInner({
90108
enableRecaptcha,
91109
onSubmitted,
92110
title,
111+
thankYouContent,
93112
}: HubSpotSsrFormProps) {
94113
const t = useTranslations("forms")
95114

@@ -119,10 +138,22 @@ function HubSpotSsrFormInner({
119138
return
120139
}
121140

141+
// HubSpot may answer with a redirect target; honour it first.
142+
const redirectUri = nonEmpty(data.redirectUri)
143+
144+
if (redirectUri) {
145+
window.location.href = redirectUri
146+
147+
return
148+
}
149+
122150
const postSubmitAction = schema.configuration?.postSubmitAction
123151

124-
if (postSubmitAction?.type === "redirect_url" && postSubmitAction.value) {
125-
window.location.href = postSubmitAction.value
152+
if (
153+
postSubmitAction?.type === "redirect_url" &&
154+
nonEmpty(postSubmitAction.value)
155+
) {
156+
window.location.href = postSubmitAction.value as string
126157
}
127158
},
128159
onError: (err) => {
@@ -156,17 +187,35 @@ function HubSpotSsrFormInner({
156187
}
157188

158189
if (submitMutation.isSuccess && !onSubmitted) {
190+
// 1. CMS-managed thank-you block (preferred: works for every HubSpot form,
191+
// including new-editor forms whose inlineMessage comes back empty).
192+
if (thankYouContent) {
193+
return <>{thankYouContent}</>
194+
}
195+
196+
// 2. HubSpot-provided HTML, skipping empty strings so the fallback below
197+
// always applies (`??` alone would render a blank box for "").
159198
const postSubmitAction = schema.configuration?.postSubmitAction
160199
const message =
161-
submitMutation.data.inlineMessage ??
162-
postSubmitAction?.value ??
163-
t("submissionSuccess")
200+
nonEmpty(submitMutation.data.inlineMessage) ??
201+
(postSubmitAction?.type !== "redirect_url"
202+
? nonEmpty(postSubmitAction?.value)
203+
: undefined)
204+
205+
if (message) {
206+
return (
207+
<div
208+
className="text-foreground text-sm leading-relaxed"
209+
dangerouslySetInnerHTML={{ __html: message }}
210+
/>
211+
)
212+
}
164213

214+
// 3. Generic, translated fallback.
165215
return (
166-
<div
167-
className="text-foreground text-sm leading-relaxed"
168-
dangerouslySetInnerHTML={{ __html: message }}
169-
/>
216+
<p className="text-foreground text-sm leading-relaxed" role="status">
217+
{t("submissionSuccess")}
218+
</p>
170219
)
171220
}
172221

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import type { Data } from "@repo/strapi-types"
2+
3+
import { Markdown } from "@/components/elementary/markdown/Markdown"
4+
import { StrapiBasicImage } from "@/components/page-builder/components/utilities/StrapiBasicImage"
5+
6+
type HubSpotFormEntry = Pick<
7+
Data.ContentType<"api::hubspot-form.hubspot-form">,
8+
"thankYouImage" | "thankYouMessage"
9+
>
10+
11+
/**
12+
* Returns true when the CMS entry carries a custom thank-you block.
13+
* HubSpot forms built in the new editor return an empty `inlineMessage`
14+
* from the legacy submissions API, so the CMS content is the reliable source.
15+
*/
16+
export function hasThankYouContent(
17+
form: Partial<HubSpotFormEntry> | null | undefined
18+
): boolean {
19+
return Boolean(form?.thankYouMessage?.trim() || form?.thankYouImage?.media)
20+
}
21+
22+
/**
23+
* Server-rendered thank-you block shown in place of the form after a
24+
* successful submission. Mirrors the "On submit" step configured in HubSpot:
25+
* an optional image (e.g. the logo) followed by a markdown message.
26+
*/
27+
export function HubSpotThankYou({
28+
form,
29+
}: {
30+
readonly form: Partial<HubSpotFormEntry> | null | undefined
31+
}) {
32+
if (!hasThankYouContent(form)) {
33+
return null
34+
}
35+
36+
return (
37+
<div className="flex flex-col items-center gap-6 py-6 text-center">
38+
{form?.thankYouImage?.media ? (
39+
<StrapiBasicImage
40+
component={form.thankYouImage}
41+
className="h-auto max-h-24 w-auto"
42+
hideWhenMissing
43+
/>
44+
) : null}
45+
46+
{form?.thankYouMessage?.trim() ? (
47+
<Markdown className="[&_p]:text-muted-foreground [&_h1]:text-xl [&_h1]:font-bold [&_h2]:text-xl [&_h2]:font-bold [&_p]:text-sm [&_p:last-child]:mb-0">
48+
{form.thankYouMessage}
49+
</Markdown>
50+
) : null}
51+
</div>
52+
)
53+
}

‎apps/ui/src/components/page-builder/components/forms/strapi-hubspot-form-ssr/StrapiHubSpotFormSsr.tsx‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { fetchHubSpotFormSchema } from "@/lib/hubspot"
66
import { logNonBlockingError } from "@/lib/logging"
77

88
import { HubSpotSsrForm } from "./HubSpotSsrForm"
9+
import { HubSpotThankYou, hasThankYouContent } from "./HubSpotThankYou"
910

1011
export async function StrapiHubSpotFormSsr({
1112
component,
@@ -49,6 +50,11 @@ export async function StrapiHubSpotFormSsr({
4950
formId={form.formId}
5051
enableRecaptcha={component.enableRecaptcha ?? false}
5152
title={component.title}
53+
thankYouContent={
54+
hasThankYouContent(form) ? (
55+
<HubSpotThankYou form={form} />
56+
) : undefined
57+
}
5258
/>
5359
</div>
5460
</Container>

‎apps/ui/src/components/page-builder/components/sections/strapi-conversion/StrapiConversion.tsx‎

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ import type { Data } from "@repo/strapi-types"
22

33
import { FormUnavailableAlert } from "@/components/elementary/FormUnavailableAlert"
44
import { HubSpotSsrForm } from "@/components/page-builder/components/forms/strapi-hubspot-form-ssr/HubSpotSsrForm"
5+
import {
6+
HubSpotThankYou,
7+
hasThankYouContent,
8+
} from "@/components/page-builder/components/forms/strapi-hubspot-form-ssr/HubSpotThankYou"
59
import { fetchHubSpotFormSchema } from "@/lib/hubspot"
610
import { logNonBlockingError } from "@/lib/logging"
711

@@ -33,7 +37,16 @@ export async function StrapiConversion({
3337

3438
const formSlot =
3539
formSchema && portalId && formId ? (
36-
<HubSpotSsrForm schema={formSchema} portalId={portalId} formId={formId} />
40+
<HubSpotSsrForm
41+
schema={formSchema}
42+
portalId={portalId}
43+
formId={formId}
44+
thankYouContent={
45+
hasThankYouContent(form?.form) ? (
46+
<HubSpotThankYou form={form?.form} />
47+
) : undefined
48+
}
49+
/>
3750
) : (
3851
<FormUnavailableAlert error={formError} />
3952
)

0 commit comments

Comments
 (0)