Skip to content

Commit c6e0d54

Browse files
committed
feat(docs): add SubscribeSection component for email subscription functionality
1 parent cdab936 commit c6e0d54

File tree

3 files changed

+236
-0
lines changed

3 files changed

+236
-0
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
.input {
2+
border: none;
3+
outline: none;
4+
5+
&::placeholder {
6+
color: var(--inverted-foreground-color);
7+
}
8+
9+
&:focus {
10+
outline: 2px solid var(--ifm-color-primary);
11+
outline-offset: 2px;
12+
}
13+
14+
&:disabled {
15+
opacity: 0.6;
16+
cursor: not-allowed;
17+
}
18+
}
19+
20+
.contactFormText {
21+
color: var(--inverted-foreground-color);
22+
}
23+
24+
.success {
25+
color: var(--inverted-foreground-color);
26+
}
27+
28+
.error {
29+
color: #d61830;
30+
}
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
import clsx from 'clsx';
2+
import React, { useState } from 'react';
3+
4+
import CircleAlertIcon from '@site/src/assets/icons/circle-alert.svg';
5+
import CircleCheckIcon from '@site/src/assets/icons/circle-check.svg';
6+
import { H2, H3 } from '@site/src/components/Typography';
7+
8+
import styles from './SubscribeSection.module.scss';
9+
10+
interface SubscribeSectionProps {
11+
portalId: string;
12+
formId: string;
13+
}
14+
15+
export const SubscribeSection: React.FC<SubscribeSectionProps> = ({ portalId, formId }) => {
16+
const [email, setEmail] = useState('');
17+
const [emailContactConsent, setEmailContactConsent] = useState(false);
18+
const [status, setStatus] = useState<{ type: 'success' | 'error' | null; message: string }>({
19+
type: null,
20+
message: '',
21+
});
22+
const [isSubmitting, setIsSubmitting] = useState(false);
23+
24+
const getPageUri = () => {
25+
if (typeof window !== 'undefined' && window.location) {
26+
return window.location.href;
27+
}
28+
return '';
29+
};
30+
31+
const handleSubmit = async (e: React.FormEvent) => {
32+
e.preventDefault();
33+
if (!emailContactConsent) {
34+
setStatus({
35+
type: 'error',
36+
message: 'Please agree to the privacy policy to subscribe.',
37+
});
38+
return;
39+
}
40+
setIsSubmitting(true);
41+
42+
const payload = {
43+
fields: [
44+
{
45+
name: 'email',
46+
value: email,
47+
},
48+
{
49+
name: 'email_contact_consent',
50+
value: emailContactConsent,
51+
},
52+
],
53+
context: {
54+
pageUri: getPageUri(),
55+
pageName: 'Homepage - Open Self Service',
56+
},
57+
legalConsentOptions: {
58+
consent: {
59+
consentToProcess: true,
60+
text: 'I consent to the processing of my personal data by Hycom SA as described in the openselfservice_EN_Information_obligation.pdf information clause to respond to inquiries and provide information about products and services.',
61+
communications: [
62+
{
63+
value: true,
64+
subscriptionTypeId: '296606641',
65+
text: 'I agree to receive communications that respond to inquiries and provide information about products and services.',
66+
},
67+
],
68+
},
69+
},
70+
};
71+
72+
const url = new URL(window.location.href);
73+
74+
try {
75+
const response = await fetch(
76+
`https://api.hsforms.com/submissions/v3/integration/submit/${portalId}/${formId}`,
77+
{
78+
method: 'POST',
79+
headers: { 'Content-Type': 'application/json' },
80+
body: JSON.stringify(payload),
81+
},
82+
);
83+
84+
if (response.ok) {
85+
const data = await response.json();
86+
setStatus({ type: 'success', message: data.inlineMessage });
87+
setEmail('');
88+
setEmailContactConsent(false);
89+
90+
url.searchParams.append('success', 'true');
91+
window.history.replaceState(null, null, url);
92+
} else {
93+
const data = await response.json();
94+
setStatus({ type: 'error', message: data.message });
95+
setIsSubmitting(false);
96+
97+
url.searchParams.append('success', 'false');
98+
window.history.replaceState(null, null, url);
99+
}
100+
} catch (error) {
101+
setStatus({
102+
type: 'error',
103+
message: 'An unexpected error occurred. Please try again later.',
104+
});
105+
setIsSubmitting(false);
106+
107+
url.searchParams.append('success', 'false');
108+
window.history.replaceState(null, null, url);
109+
}
110+
};
111+
112+
return (
113+
<div
114+
className={clsx(
115+
'flex flex-col md:flex-row gap-12 md:gap-28 items-start justify-start w-full',
116+
styles.container,
117+
)}
118+
>
119+
{/* Text Content */}
120+
<div className="flex flex-col gap-10 items-start justify-start flex-1 md:flex-1">
121+
<H2 className="mb-0!">
122+
<span className="text-highlighted">Be the first</span> to know about the latest updates, features,
123+
and insights
124+
</H2>
125+
</div>
126+
127+
{/* Form */}
128+
<div className="w-full md:w-auto md:flex-1 md:max-w-none">
129+
{status.type !== 'success' && (
130+
<form onSubmit={handleSubmit} className="flex flex-col gap-6">
131+
<div>
132+
<H3 className="mb-6!">Subscribe now</H3>
133+
<div className="flex gap-2">
134+
<label htmlFor="subscribe-email" className="sr-only">
135+
Your email address
136+
</label>
137+
<input
138+
id="subscribe-email"
139+
type="email"
140+
value={email}
141+
onChange={(e) => setEmail(e.target.value)}
142+
placeholder="Your email address"
143+
className={clsx(
144+
'flex-1 h-10 px-3 py-2 rounded-md bg-white text-sm text-gray-900',
145+
styles.input,
146+
)}
147+
required
148+
disabled={isSubmitting}
149+
/>
150+
<button type="submit" className="button text-sm!" disabled={isSubmitting}>
151+
Subscribe
152+
</button>
153+
</div>
154+
</div>
155+
156+
<div className="flex items-start gap-2">
157+
<input
158+
type="checkbox"
159+
id="subscribe-consent"
160+
checked={emailContactConsent}
161+
onChange={(e) => setEmailContactConsent(e.target.checked)}
162+
className="mt-0.5 accent-violet"
163+
required
164+
disabled={isSubmitting}
165+
/>
166+
<label htmlFor="subscribe-consent" className="text-sm text-white cursor-pointer leading-5">
167+
I consent to the processing of my personal data by Hycom&nbsp;SA as described in the{' '}
168+
<a
169+
href="/docs/openselfservice_EN_Information_obligation.pdf"
170+
target="_blank"
171+
className="underline"
172+
>
173+
information clause
174+
</a>{' '}
175+
to respond to inquiries and provide information about products and services.
176+
</label>
177+
</div>
178+
</form>
179+
)}
180+
181+
{status.type && (
182+
<div className="bg-white rounded-xl shadow-md p-8">
183+
<div className={clsx('flex items-start gap-2', styles.contactFormText)}>
184+
{status.type === 'success' ? (
185+
<CircleCheckIcon className="h-6 w-6 shrink-0 mt-0.5" />
186+
) : (
187+
<CircleAlertIcon className="h-6 w-6 shrink-0 mt-0.5" />
188+
)}
189+
<p
190+
className={clsx('mb-0!', status.type === 'success' ? styles.success : styles.error)}
191+
dangerouslySetInnerHTML={{ __html: status.message }}
192+
/>
193+
</div>
194+
</div>
195+
)}
196+
</div>
197+
</div>
198+
);
199+
};

apps/docs/src/pages/index.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { HomepageBenefitsSection } from '@site/src/components/HomepageBenefitsSe
1818
import { HomepageFeaturesSection } from '@site/src/components/HomepageFeaturesSection';
1919
import { HomepageStartersSection } from '@site/src/components/HomepageStartersSection';
2020
import { HomepageUseCases } from '@site/src/components/HomepageUseCases';
21+
import { SubscribeSection } from '@site/src/components/SubscribeSection';
2122
import { Body, BodyBold } from '@site/src/components/Typography';
2223

2324
import Layout from '@theme/Layout';
@@ -105,6 +106,12 @@ export default function Home(): ReactNode {
105106
</div>
106107
</div>
107108

109+
<div className="section-gradient-1 py-40 mb-0!">
110+
<section className="mb-0! px-4 max-w-[1080px] mx-auto">
111+
<SubscribeSection portalId="143969481" formId="ad91735e-018c-4a60-a749-18f11d57b0e4" />
112+
</section>
113+
</div>
114+
108115
{/* <HomepageAboutSection /> */}
109116
{/* <HomepageBenefitsSection /> */}
110117
</div>

0 commit comments

Comments
 (0)