style: complete redo of the email layouts (#20)
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import React from 'react';
|
||||
import Note from './components/Note';
|
||||
import Template from './Template';
|
||||
|
||||
@@ -8,21 +9,32 @@ export default function ConfirmationTemplate(code: string) {
|
||||
<Template
|
||||
title='Welcome!'
|
||||
body={
|
||||
<div style={{ fontSize: '1rem', color: '#4a5568' }}>
|
||||
<div style={{ color: '#4A5568' }}>
|
||||
<p>Dear subscriber,</p>
|
||||
<p style={{ marginTop: '0.5rem' }}>
|
||||
thank you for subscribing to our newsletter! Please click the link
|
||||
<p style={{ marginTop: '16px' }}>
|
||||
Thank you for subscribing to our newsletter! Please click the link
|
||||
below to confirm your subscription.
|
||||
</p>
|
||||
<div
|
||||
style={{
|
||||
margin: '2rem 0',
|
||||
display: 'flex',
|
||||
justifyContent: 'center'
|
||||
margin: '32px 0',
|
||||
textAlign: 'center'
|
||||
}}
|
||||
>
|
||||
<a href={`${process.env.HOME_URL}/confirmation?code=${code}`}>
|
||||
{'Confirm subscription'}
|
||||
<a
|
||||
href={`${process.env.HOME_URL}/confirmation?code=${code}`}
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
padding: '12px 24px',
|
||||
backgroundColor: '#9333EA',
|
||||
color: 'white',
|
||||
textDecoration: 'none',
|
||||
borderRadius: '6px',
|
||||
fontWeight: '500',
|
||||
border: 'none'
|
||||
}}
|
||||
>
|
||||
Confirm subscription
|
||||
</a>
|
||||
</div>
|
||||
<Note>
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import { getSayings } from '@utils/getSayings';
|
||||
import React from 'react';
|
||||
import { summirize } from '@utils/summarize';
|
||||
import { NewsType } from '@utils/validationSchemas';
|
||||
import createDOMPurify from 'isomorphic-dompurify';
|
||||
import Template from './Template';
|
||||
import extractMainTopic from '@utils/extractMainTopic';
|
||||
|
||||
export default async function NewsletterTemplate(stories: NewsType[]) {
|
||||
const summary = await summirize(stories);
|
||||
const sanitizedSummary = createDOMPurify.sanitize(summary, {
|
||||
USE_PROFILES: { html: true }
|
||||
USE_PROFILES: { html: true },
|
||||
ADD_ATTR: ['target', 'rel', 'style']
|
||||
});
|
||||
|
||||
if (!sanitizedSummary) {
|
||||
@@ -15,17 +17,51 @@ export default async function NewsletterTemplate(stories: NewsType[]) {
|
||||
throw new Error('Failed to sanitize summary');
|
||||
}
|
||||
|
||||
const topic = extractMainTopic(sanitizedSummary);
|
||||
|
||||
return {
|
||||
subject: `What's new from the Hackernews forum?`,
|
||||
subject: topic,
|
||||
template: (
|
||||
<Template
|
||||
title={`Here is something
|
||||
${getSayings[Math.floor(Math.random() * getSayings.length)]}!`}
|
||||
variant='newsletter'
|
||||
title='Your Daily Tech Updates'
|
||||
body={
|
||||
<div
|
||||
style={{ fontSize: '1rem', color: '#4a5568' }}
|
||||
dangerouslySetInnerHTML={{ __html: sanitizedSummary }}
|
||||
/>
|
||||
style={{
|
||||
color: '#374151',
|
||||
fontSize: '16px',
|
||||
lineHeight: 1.6
|
||||
}}
|
||||
>
|
||||
<style>
|
||||
{`
|
||||
.what-to-watch {
|
||||
margin-top: 32px;
|
||||
padding: 24px;
|
||||
background: linear-gradient(135deg, rgba(186,27,88,0.03) 0%, rgba(245,161,98,0.03) 100%);
|
||||
border-left: 3px solid #BA1B58;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.what-to-watch h3 {
|
||||
margin: 0 0 16px 0;
|
||||
color: #BA1B58;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
.what-to-watch p {
|
||||
margin: 0;
|
||||
color: #4A5568;
|
||||
}
|
||||
.what-to-watch a {
|
||||
color: #9333EA !important;
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
<div dangerouslySetInnerHTML={{ __html: sanitizedSummary }} />
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -1,31 +1,76 @@
|
||||
import React from 'react';
|
||||
import Footer from './components/Footer';
|
||||
|
||||
interface TemplateProps {
|
||||
title: string;
|
||||
body: JSX.Element;
|
||||
variant?: string;
|
||||
}
|
||||
|
||||
export default function Template({ title, body }: TemplateProps) {
|
||||
export default function Template({
|
||||
title,
|
||||
body,
|
||||
variant = 'default'
|
||||
}: TemplateProps) {
|
||||
const isNewsletter = variant === 'newsletter';
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
maxWidth: '720px',
|
||||
alignContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#F9FBFB'
|
||||
margin: '0 auto',
|
||||
fontFamily: 'system-ui, -apple-system, sans-serif',
|
||||
backgroundColor: '#ffffff'
|
||||
}}
|
||||
>
|
||||
<h2
|
||||
<div
|
||||
style={{
|
||||
padding: '20px',
|
||||
textAlign: 'center',
|
||||
color: 'white',
|
||||
backgroundColor: `#8230CC`
|
||||
background: 'linear-gradient(135deg, #9333EA 0%, #F5A162 100%)',
|
||||
padding: '28px 24px',
|
||||
borderTopLeftRadius: '8px',
|
||||
borderTopRightRadius: '8px'
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
</h2>
|
||||
<div style={{ margin: '20px', padding: '20px' }}>{body}</div>
|
||||
<p
|
||||
style={{
|
||||
margin: '0 0 8px 0',
|
||||
fontSize: '12px',
|
||||
textTransform: 'uppercase',
|
||||
color: 'rgba(255, 255, 255, 0.9)',
|
||||
textAlign: 'center'
|
||||
}}
|
||||
>
|
||||
Hackernews + newsletter
|
||||
</p>
|
||||
<h2
|
||||
style={{
|
||||
fontSize: '24px',
|
||||
textAlign: 'center',
|
||||
color: 'white',
|
||||
fontWeight: '600',
|
||||
margin: '0',
|
||||
letterSpacing: '-0.02em'
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
</h2>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: '#ffffff',
|
||||
padding: '32px 24px'
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
color: '#374151',
|
||||
fontSize: isNewsletter ? '16px' : '14px',
|
||||
lineHeight: '1.6'
|
||||
}}
|
||||
>
|
||||
{body}
|
||||
</div>
|
||||
</div>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import React from 'react';
|
||||
import Note from './components/Note';
|
||||
import Template from './Template';
|
||||
|
||||
@@ -6,27 +7,43 @@ export default function UnsubscribeTemplate() {
|
||||
subject: 'Unsubscribe confirmation',
|
||||
template: (
|
||||
<Template
|
||||
title="We're sad to see you go :("
|
||||
title="We're sad to see you go"
|
||||
body={
|
||||
<div style={{ fontSize: '1rem', color: '#4a5568' }}>
|
||||
<p style={{ marginTop: '0.5rem' }}>
|
||||
<div style={{ color: '#4A5568' }}>
|
||||
<p style={{ marginTop: '16px' }}>
|
||||
You have been successfully unsubscribed from our newsletter. You
|
||||
won't receive any further communications from us unless you
|
||||
explicitly opt-in again.
|
||||
</p>
|
||||
<div
|
||||
style={{
|
||||
margin: '2rem 0',
|
||||
display: 'flex',
|
||||
justifyContent: 'center'
|
||||
margin: '32px 0',
|
||||
textAlign: 'center'
|
||||
}}
|
||||
>
|
||||
<a href={`${process.env.HOME_URL}/`}>{'Re-subscribe'}</a>
|
||||
<a
|
||||
href={`${process.env.HOME_URL}/`}
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
padding: '12px 24px',
|
||||
backgroundColor: '#9333EA',
|
||||
color: 'white',
|
||||
textDecoration: 'none',
|
||||
borderRadius: '6px',
|
||||
fontWeight: '500',
|
||||
border: 'none'
|
||||
}}
|
||||
>
|
||||
Re-subscribe
|
||||
</a>
|
||||
</div>
|
||||
<Note>
|
||||
If you have any questions or concerns, please feel free to{' '}
|
||||
<a
|
||||
style={{ color: '#3b82f6', textDecoration: 'underline' }}
|
||||
style={{
|
||||
color: '#386FA4',
|
||||
textDecoration: 'none'
|
||||
}}
|
||||
href={`mailto:${process.env.NEXT_PUBLIC_BRAND_EMAIL}`}
|
||||
>
|
||||
contact us
|
||||
|
||||
@@ -1,35 +1,167 @@
|
||||
import {
|
||||
User,
|
||||
Building2,
|
||||
Mail,
|
||||
LogOut,
|
||||
LayoutGrid,
|
||||
Shield,
|
||||
Home
|
||||
} from 'lucide-react';
|
||||
|
||||
export default function Footer() {
|
||||
return (
|
||||
<footer
|
||||
style={{
|
||||
background: 'linear-gradient(180deg, #F8FAFC 0%, #F1F4F9 100%)',
|
||||
paddingTop: '1.5rem',
|
||||
backgroundColor: `#E6F2F2`,
|
||||
color: 'black'
|
||||
borderBottomLeftRadius: '8px',
|
||||
borderBottomRightRadius: '8px',
|
||||
borderLeft: '1px solid #E5E9F0',
|
||||
borderRight: '1px solid #E5E9F0',
|
||||
borderBottom: '1px solid #E5E9F0'
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
marginLeft: '2rem',
|
||||
margin: '0 2rem',
|
||||
paddingBottom: '2rem',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
paddingBottom: '2rem'
|
||||
justifyContent: 'space-between'
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<h4 style={{ fontSize: '1.125rem', fontWeight: '600' }}>
|
||||
<h4
|
||||
style={{
|
||||
fontSize: '14px',
|
||||
fontWeight: '600',
|
||||
marginBottom: '1rem',
|
||||
marginTop: '0',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '8px',
|
||||
color: '#2F4858',
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: '0.05em'
|
||||
}}
|
||||
>
|
||||
<User size={16} color='#386FA4' />
|
||||
Contact Us
|
||||
</h4>
|
||||
<p>{process.env.NEXT_PUBLIC_BRAND_NAME}</p>
|
||||
<p>
|
||||
Email:{' '}
|
||||
<a href={`mailto:${process.env.NEXT_PUBLIC_BRAND_EMAIL}`}>
|
||||
<p
|
||||
style={{
|
||||
margin: '0.5rem 0',
|
||||
fontSize: '13px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '8px',
|
||||
color: '#4A5568'
|
||||
}}
|
||||
>
|
||||
<Building2 size={14} color='#386FA4' />
|
||||
{process.env.NEXT_PUBLIC_BRAND_NAME}
|
||||
</p>
|
||||
<p
|
||||
style={{
|
||||
margin: '0.5rem 0',
|
||||
fontSize: '13px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '8px',
|
||||
color: '#4A5568'
|
||||
}}
|
||||
>
|
||||
<Mail size={14} color='#386FA4' />
|
||||
<a
|
||||
href={`mailto:${process.env.NEXT_PUBLIC_BRAND_EMAIL}`}
|
||||
style={{ color: '#386FA4', textDecoration: 'none' }}
|
||||
>
|
||||
{process.env.NEXT_PUBLIC_BRAND_EMAIL}
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
Click <a href={`${process.env.HOME_URL}/unsubscribe`}>here</a> to
|
||||
unsubscribe.
|
||||
<p
|
||||
style={{
|
||||
margin: '0.5rem 0',
|
||||
fontSize: '13px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '8px',
|
||||
color: '#4A5568'
|
||||
}}
|
||||
>
|
||||
<LogOut size={14} color='#386FA4' />
|
||||
<span>
|
||||
Click{' '}
|
||||
<a
|
||||
href={`${process.env.HOME_URL}/unsubscribe`}
|
||||
style={{ color: '#386FA4', textDecoration: 'none' }}
|
||||
>
|
||||
here
|
||||
</a>{' '}
|
||||
to unsubscribe
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
width: '1px',
|
||||
background: 'linear-gradient(180deg, #E5E9F0 0%, transparent 100%)',
|
||||
alignSelf: 'stretch',
|
||||
height: 'auto'
|
||||
}}
|
||||
/>
|
||||
<div>
|
||||
<h4
|
||||
style={{
|
||||
fontSize: '14px',
|
||||
fontWeight: '600',
|
||||
marginBottom: '1rem',
|
||||
marginTop: '0',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '8px',
|
||||
color: '#2F4858',
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: '0.05em'
|
||||
}}
|
||||
>
|
||||
<LayoutGrid size={16} color='#386FA4' />
|
||||
Quick Links
|
||||
</h4>
|
||||
<p
|
||||
style={{
|
||||
margin: '0.5rem 0',
|
||||
fontSize: '13px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '8px',
|
||||
color: '#4A5568'
|
||||
}}
|
||||
>
|
||||
<Shield size={14} color='#386FA4' />
|
||||
<a
|
||||
href={`${process.env.HOME_URL}/privacy`}
|
||||
style={{ color: '#386FA4', textDecoration: 'none' }}
|
||||
>
|
||||
Privacy Policy
|
||||
</a>
|
||||
</p>
|
||||
<p
|
||||
style={{
|
||||
margin: '0.5rem 0',
|
||||
fontSize: '13px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '8px',
|
||||
color: '#4A5568'
|
||||
}}
|
||||
>
|
||||
<Home size={14} color='#386FA4' />
|
||||
<a
|
||||
href={process.env.HOME_URL}
|
||||
style={{ color: '#386FA4', textDecoration: 'none' }}
|
||||
>
|
||||
Visit Website
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,15 +4,8 @@ interface NoteProps {
|
||||
|
||||
export default function Note({ children }: NoteProps) {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
marginTop: '2rem',
|
||||
padding: '1.5rem 1.5rem',
|
||||
backgroundColor: '#EBF1F5',
|
||||
color: '#718096'
|
||||
}}
|
||||
>
|
||||
<div style={{ fontSize: '0.875rem' }}>{children}</div>
|
||||
<div className='mt-6 rounded-md bg-gray-50 p-4 text-sm text-gray-600'>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user