Skip to content

Commit 69c96ee

Browse files
Standardize newsletters (#322)
* Adjust and comment out newsletter code in use * Fix commented out code * Add astro action for newsletter * Modify newslettersection to use the action and have cleaned up logic * Uncomment NewsletterSection in ArticleLayout * Complete newsletter action arguments * Connect action to cf worker * Hide newsletter form in ArticleLayout * Move from actions to inline and fix up minor issues --------- Co-authored-by: wkenned1 <[email protected]>
1 parent 50ef90c commit 69c96ee

File tree

10 files changed

+225
-153
lines changed

10 files changed

+225
-153
lines changed

src/components/AuthorWide.astro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ const { author, loading = "lazy" } = Astro.props,
3030
: undefined;
3131
---
3232

33-
<div class="my-8">
33+
<div class="mt-8 md:mt-12">
3434
<AuthorWideHelper
3535
authorId={author.id}
3636
{biography}

src/components/NewsletterForm.astro

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,6 @@
4040
<div id="mce-error-response" style={`display: "none"`}></div>
4141
<div id="mce-success-response" style={`display: "none"`}></div>
4242
</div>
43-
<script
44-
is:inline
45-
type="text/javascript"
46-
src="//s3.amazonaws.com/downloads.mailchimp.com/js/mc-validate.js"
47-
defer></script>
4843
</form>
4944
</div>
5045
</div>
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
---
2+
interface Props {
3+
stacked?: boolean;
4+
title?: string;
5+
}
6+
7+
const { stacked = false, title } = Astro.props;
8+
---
9+
10+
{
11+
stacked ? (
12+
<div class="bg-bkg py-8 md:py-0">
13+
<div class="mx-auto max-w-7xl px-6">
14+
{title && (
15+
<h2 class="max-w-xl text-balance text-xl font-semibold tracking-tight text-content-1 sm:text-2xl">
16+
{title}
17+
</h2>
18+
)}
19+
<form id="newsletter-form" class="w-full max-w-md pt-2 lg:col-span-5">
20+
<div class="flex gap-x-4">
21+
<label for="email" class="sr-only">
22+
{" "}
23+
Email address{" "}
24+
</label>
25+
<input
26+
id="email"
27+
name="email"
28+
type="email"
29+
autocomplete="email"
30+
required
31+
class="min-w-0 flex-auto rounded-md bg-white px-3.5 py-2 text-base text-black placeholder-black outline outline-1 -outline-offset-1 outline-gray-300 placeholder:text-black focus:outline focus:outline-2 focus:-outline-offset-2 focus:outline-indigo-600 sm:text-sm/6"
32+
placeholder="Enter your email"
33+
/>
34+
<button
35+
type="submit"
36+
class="flex-none rounded-md bg-cta px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-accent-1 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
37+
>
38+
Subscribe
39+
</button>
40+
</div>
41+
<p class="mt-4 text-sm/6 text-content-1">
42+
This site is built on trust, and your data is safe. Check out the{" "}
43+
<a href="/policies/privacy" class="font-semibold">
44+
privacy&nbsp;policy
45+
</a>
46+
.
47+
</p>
48+
</form>
49+
<p
50+
id="confirmation-message"
51+
class="mt-4 hidden text-sm/6 text-green-200"
52+
>
53+
Thank you for subscribing! Check your email for confirmation.
54+
</p>
55+
</div>
56+
</div>
57+
) : (
58+
<div class="bg-bkg py-8 sm:py-12 lg:py-16">
59+
<div class="mx-auto grid max-w-7xl grid-cols-1 gap-10 px-6 lg:grid-cols-12 lg:gap-8 lg:px-8">
60+
<h2 class="max-w-xl text-balance text-3xl font-semibold tracking-tight text-content-1 sm:text-4xl lg:col-span-7">
61+
Like what you see? Keep learning by subscribing to the newsletter!
62+
</h2>
63+
<form
64+
id="newsletter-form"
65+
class="w-full max-w-md lg:col-span-5 lg:pt-2"
66+
>
67+
<div class="flex gap-x-4">
68+
<label for="email" class="sr-only">
69+
{" "}
70+
Email address{" "}
71+
</label>
72+
<input
73+
id="email"
74+
name="email"
75+
type="email"
76+
autocomplete="email"
77+
required
78+
class="min-w-0 flex-auto rounded-md bg-white px-3.5 py-2 text-base text-black placeholder-black outline outline-1 -outline-offset-1 outline-gray-300 placeholder:text-black focus:outline focus:outline-2 focus:-outline-offset-2 focus:outline-indigo-600 sm:text-sm/6"
79+
placeholder="Enter your email"
80+
/>
81+
<button
82+
type="submit"
83+
class="flex-none rounded-md bg-cta px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-accent-1 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
84+
>
85+
Subscribe
86+
</button>
87+
</div>
88+
<p class="mt-4 text-sm/6 text-content-1">
89+
This site is built on trust, and your data is safe. Check out the{" "}
90+
<a href="/policies/privacy" class="font-semibold">
91+
privacy&nbsp;policy
92+
</a>
93+
.
94+
</p>
95+
</form>
96+
<p
97+
id="confirmation-message"
98+
class="mt-4 hidden text-sm/6 text-green-200"
99+
>
100+
Thank you for subscribing! Check your email for confirmation.
101+
</p>
102+
</div>
103+
</div>
104+
)
105+
}
106+
107+
<script>
108+
import { z } from "astro/zod";
109+
110+
const subscribeToNewsletterSchema = z.object({
111+
email: z.string().email(),
112+
}),
113+
subscribeToNewsletter = async (email: string) => {
114+
const url = "https://rtjvm-mailerlite-worker.andrei-023.workers.dev",
115+
route = "/",
116+
body = JSON.stringify({
117+
email,
118+
}),
119+
response = await fetch(url + route, {
120+
method: "POST",
121+
headers: {
122+
"Content-Type": "application/json",
123+
Accept: "application/json",
124+
},
125+
body,
126+
});
127+
128+
if (!response.ok) {
129+
throw new Error(
130+
"Failed to subscribe to the newsletter. Try again later.",
131+
);
132+
}
133+
134+
return;
135+
};
136+
137+
document.addEventListener("DOMContentLoaded", () => {
138+
const form: HTMLFormElement = document.querySelector("#newsletter-form")!,
139+
confirmationMessage = document.querySelector("#confirmation-message")!;
140+
141+
form?.addEventListener("submit", async (event) => {
142+
event.preventDefault();
143+
const formData = new FormData(form);
144+
145+
const { email } = subscribeToNewsletterSchema.parse({
146+
email: formData.get("email"),
147+
});
148+
149+
try {
150+
await subscribeToNewsletter(email);
151+
form.style.display = "none";
152+
confirmationMessage.classList.remove("hidden");
153+
} catch (_) {
154+
alert("Failed to subscribe to the newsletter. Please try again later.");
155+
}
156+
});
157+
});
158+
</script>

src/layouts/BaseLayout/BaseLayout.astro

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ interface Props {
2727
canonicalUrl?: URL;
2828
forcedTheme?: string;
2929
description: string;
30+
hideFooterNewsletter?: boolean;
3031
image?: {
3132
alt: string;
3233
src: string;
@@ -48,6 +49,7 @@ const {
4849
error = false,
4950
image = site.logo,
5051
isHomePage = false,
52+
hideFooterNewsletter = false,
5153
structuredData,
5254
title,
5355
type = "website",
@@ -75,7 +77,10 @@ const {
7577
<slot />
7678
<CookieBanner client:load />
7779
</main>
78-
<Footer holiday={Boolean(forcedTheme)} />
80+
<Footer
81+
hideNewsletter={hideFooterNewsletter}
82+
holiday={Boolean(forcedTheme)}
83+
/>
7984
</body>
8085
</html>
8186
<AffiliateScript />

src/layouts/BaseLayout/_sections/Footer/Footer.astro

Lines changed: 9 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ import LinkedIn from "@/components/icons/LinkedIn.astro";
55
import RSS from "@/components/icons/RSS.astro";
66
import Twitter from "@/components/icons/Twitter.astro";
77
import YouTube from "@/components/icons/YouTube.astro";
8+
import NewsletterSection from "@/components/NewsletterSection.astro";
89
import FooterHelper from "./FooterHelper";
910
1011
interface Props {
12+
hideNewsletter?: boolean;
1113
holiday?: boolean;
1214
}
1315
14-
const { holiday } = Astro.props;
16+
const { hideNewsletter = false, holiday } = Astro.props;
1517
---
1618

1719
<FooterHelper client:visible {holiday}>
@@ -22,66 +24,12 @@ const { holiday } = Astro.props;
2224
<YouTube slot="youtube" />
2325
<RSS slot="rss" />
2426
{
25-
!holiday && (
26-
<div id="mc_embed_shell" slot="newsletter">
27-
<div id="mc_embed_signup">
28-
<form
29-
action="https://rockthejvm.us5.list-manage.com/subscribe/post?u=f7e7dcf30c1dd4f49893c696b&id=2c292e211e&f_id=003d27ebf0"
30-
method="post"
31-
id="mc-embedded-subscribe-form"
32-
name="mc-embedded-subscribe-form"
33-
target="_blank"
34-
>
35-
<div id="mc_embed_signup_scroll">
36-
<h3
37-
class={`text-sm font-semibold leading-6 ${holiday ? "text-holiday-content-1" : "text-content"}`}
38-
>
39-
Subscribe to our newsletter
40-
</h3>
41-
<div>
42-
<input
43-
type="email"
44-
name="EMAIL"
45-
class="w-full min-w-0 appearance-none rounded-md border-0 bg-white px-3 py-1.5 text-base text-black shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-black focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:w-64 sm:text-sm sm:leading-6 xl:w-full"
46-
id="mce-EMAIL"
47-
required=""
48-
value=""
49-
placeholder="Enter your email"
50-
/>
51-
</div>
52-
<div id="mce-responses">
53-
<div id="mce-error-response" style="display: none;" />
54-
<div id="mce-success-response" style="display: none;" />
55-
</div>
56-
<div
57-
aria-hidden="true"
58-
style="position: absolute; left: -5000px;"
59-
>
60-
<input
61-
type="text"
62-
name="b_f7e7dcf30c1dd4f49893c696b_2c292e211e"
63-
tabindex="-1"
64-
value=""
65-
/>
66-
</div>
67-
<div class="my-2">
68-
<input
69-
type="submit"
70-
name="subscribe"
71-
id="mc-embedded-subscribe"
72-
class="flex items-center justify-center rounded-md bg-cta px-3 py-2 text-sm font-semibold text-ctatext shadow-sm hover:bg-accent-1 hover:text-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
73-
value="Subscribe"
74-
/>
75-
</div>
76-
</div>
77-
</form>
78-
</div>
79-
<script
80-
type="text/javascript"
81-
src="//s3.amazonaws.com/downloads.mailchimp.com/js/mc-validate.js"
82-
defer
83-
/>
84-
</div>
27+
!holiday && !hideNewsletter && (
28+
<NewsletterSection
29+
stacked={true}
30+
title="Subscribe to our newsletter!"
31+
slot="newsletter"
32+
/>
8533
)
8634
}
8735
</FooterHelper>

src/layouts/BaseLayout/_sections/Footer/FooterHelper.jsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,12 @@ export default function Example(props) {
118118
<h2 id="footer-heading" className="sr-only">
119119
Footer
120120
</h2>
121-
<div className="mx-auto max-w-7xl px-6 pb-8 pt-20 sm:pt-24 lg:px-8 lg:pt-32">
122-
<div className="xl:grid xl:grid-cols-3 xl:gap-8">
123-
<div className="grid grid-cols-2 gap-8 xl:col-span-2">
121+
<div className="mx-auto w-full max-w-7xl px-6 pb-8 pt-20 sm:pt-24 lg:px-8 lg:pt-32">
122+
<div className="w-full xl:grid xl:grid-cols-4 xl:gap-8">
123+
<div className="col-span-2 mb-8 mt-10 w-full justify-center xl:order-2 xl:mb-0 xl:mt-0 xl:flex xl:w-auto">
124+
{props.newsletter}
125+
</div>
126+
<div className="grid grid-cols-2 gap-8 xl:order-1 xl:col-span-2">
124127
<div className="md:grid md:grid-cols-2 md:gap-8">
125128
<div>
126129
<h3
@@ -215,7 +218,6 @@ export default function Example(props) {
215218
</div>
216219
</div>
217220
</div>
218-
<div className="mt-10 xl:mt-0">{props.newsletter}</div>
219221
</div>
220222
<div className="mt-16 border-t border-gray-900/10 pt-8 sm:mt-20 md:flex md:items-center md:justify-between lg:mt-24">
221223
<ul className="flex space-x-6 md:order-2">

src/layouts/HolidayLayout/_components/HolidayCourseList.astro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,5 @@ const { categories, couponCode } = Astro.props;
2424
</section>
2525
))
2626
}
27-
<NewsletterForm />
27+
<!-- <NewsletterForm /> -->
2828
</article>

src/pages/articles/_components/NewsletterSection.astro

Lines changed: 0 additions & 36 deletions
This file was deleted.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
import ArticlePreview from "@/components/ArticlePreview.astro";
3+
import type { CollectionEntry } from "astro:content";
4+
import RelatedArticles from "./RelatedArticles";
5+
6+
interface Props {
7+
relatedArticles: CollectionEntry<"articles">[];
8+
}
9+
const { relatedArticles } = Astro.props;
10+
---
11+
12+
{
13+
relatedArticles.length > 0 && (
14+
<RelatedArticles length={relatedArticles.length} client:load>
15+
{relatedArticles.length >= 1 && (
16+
<ArticlePreview slot="firstArticle" article={relatedArticles[0]!} />
17+
)}
18+
{relatedArticles.length >= 2 && (
19+
<ArticlePreview slot="secondArticle" article={relatedArticles[1]!} />
20+
)}
21+
{relatedArticles.length >= 3 && (
22+
<ArticlePreview slot="thirdArticle" article={relatedArticles[2]!} />
23+
)}
24+
{relatedArticles.length >= 4 && (
25+
<ArticlePreview slot="fourthArticle" article={relatedArticles[3]!} />
26+
)}
27+
{relatedArticles.length >= 5 && (
28+
<ArticlePreview slot="fifthArticle" article={relatedArticles[4]!} />
29+
)}
30+
</RelatedArticles>
31+
)
32+
}

0 commit comments

Comments
 (0)