style: tweaked email templates

This commit is contained in:
Riccardo
2023-12-17 19:10:05 +01:00
parent 878e787ed0
commit a9b2c94cff
6 changed files with 148 additions and 63 deletions

View File

@@ -0,0 +1,14 @@
export function Footer() {
return (
<footer className='mt-8 border-t border-gray-200 pt-6'>
<div className='mb-4 ml-8 flex items-center justify-between'>
<div>
<h4 className='text-lg font-semibold text-gray-700'>Contact Us</h4>
<p className='text-gray-600'>FromPixels</p>
<p className='text-gray-600'>Email: info@frompixels.com</p>
</div>
</div>
<p className='text-sm text-gray-500'></p>
</footer>
);
}

View File

@@ -0,0 +1,11 @@
type NoteProps = {
children: React.ReactNode;
};
export function Note({ children }: NoteProps) {
return (
<div className='mt-8 bg-gray-100 px-6 py-4 dark:bg-gray-800'>
<div className='text-sm text-gray-600 dark:text-gray-400'>{children}</div>
</div>
);
}

View File

@@ -1,3 +1,5 @@
import { Link } from '../custom/link';
import { Note } from './components/note';
import Email from './template'; import Email from './template';
export default function ConfirmationTemplate(code: string) { export default function ConfirmationTemplate(code: string) {
@@ -7,14 +9,25 @@ export default function ConfirmationTemplate(code: string) {
<Email <Email
title={'Welcome!'} title={'Welcome!'}
body={ body={
<> <div className='mt-8'>
Thank you for subscribing. Please confirm your email address by <p className='text-base text-gray-700 dark:text-gray-400'>
clicking{' '} Dear subscriber,
<a href={`${process.env.HOME_URL}/confirmation?code=${code}`}> </p>
here <p className='mt-2 text-base text-gray-700 dark:text-gray-400'>
</a> thank you for subscribing to our newsletter! Please click the
. button below to confirm your subscription.
</> </p>
<div className='mt-8 flex justify-center'>
<Link
path={`${process.env.HOME_URL}/confirmation?code=${code}`}
text='Confirm Subscription'
/>
</div>
<Note>
If you didn&apos;t subscribe to our newsletter, please ignore this
email.
</Note>
</div>
} }
/> />
) )

View File

@@ -4,51 +4,85 @@ import { Section } from '@react-email/section';
import { Text } from '@react-email/text'; import { Text } from '@react-email/text';
import { z } from 'zod'; import { z } from 'zod';
import { NewsSchema } from '../../utils/schemas'; import { NewsSchema } from '../../utils/schemas';
import { Footer } from './components/footer';
export default function NewsletterTemplate( export default function NewsletterTemplate(
stories: z.infer<typeof NewsSchema>[] stories: z.infer<typeof NewsSchema>[]
) { ) {
const sayings = [
'hot off the press',
'straight from the oven',
"straight from the horse's mouth",
'brand spanking new',
'fresh as a daisy',
'straight out of the box',
'straight off the assembly line',
'hot out of the kitchen',
'just minted',
'freshly brewed',
'just off the production line'
];
return { return {
subject: `What's new from Hackernews?`, subject: `What's new from Hackernews?`,
template: ( template: (
<Html> <Html>
<Section style={main}> <Section style={main}>
<Container style={container}> <div className='mx-auto w-full max-w-2xl overflow-hidden rounded-lg bg-white shadow-lg'>
<Text style={paragraph}> <div className='text-center '>
{stories.map(story => { <h1 className='my-4 text-3xl font-bold'>Good day!</h1>
return ( <p>
<div Here is something{' '}
key={story.id} {sayings[Math.floor(Math.random() * sayings.length)]}:
style={{ </p>
padding: '10px', </div>
border: '1px solid #ccc', <Container style={container}>
boxShadow: '2px 2px 5px rgba(0, 0, 0, 0.1)' <Text>
}} {stories.map(story => {
> return (
<h1>{story.title}</h1> <div
<p>{story.by}</p> key={story.id}
{story.text && ( className='mt-8 rounded-lg border bg-card text-card-foreground shadow-sm'
<p data-v0-t='card'
dangerouslySetInnerHTML={{ >
__html: <div className='flex flex-col space-y-1.5 p-6'>
story.text.length > 500 <h2 className='text-2xl font-semibold'>
? story.text.substring(0, 500) + '...' {story.title}
: story.text </h2>
}} <p>{story.by}</p>
/> </div>
)} {story.text && (
{story.url && ( <div className='px-6'>
<p> <p
<a href={story.url} style={{ textAlign: 'right' }}> className='mb-4'
Read more dangerouslySetInnerHTML={{
</a> __html:
</p> story.text.length > 500
)} ? story.text.substring(0, 500) + '...'
</div> : story.text
); }}
})} />
</Text> </div>
</Container> )}
{story.url && (
<div className='p-4 text-right'>
<p>
<a
href={story.url}
className='inline-flex h-10 items-center justify-center rounded-md bg-blue-500 px-4 py-2 text-sm font-medium text-primary-foreground ring-offset-background transition-colors hover:bg-blue-700/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50'
>
Read more
</a>
</p>
</div>
)}
</div>
);
})}
</Text>
</Container>
<Footer />
</div>
</Section> </Section>
</Html> </Html>
) )
@@ -64,9 +98,3 @@ const container = {
padding: '20px 0 48px', padding: '20px 0 48px',
width: '580px' width: '580px'
}; };
const paragraph = {
fontSize: '18px',
lineHeight: '1.4',
color: '#484848'
};

View File

@@ -2,6 +2,7 @@ import { Container } from '@react-email/container';
import { Html } from '@react-email/html'; import { Html } from '@react-email/html';
import { Section } from '@react-email/section'; import { Section } from '@react-email/section';
import { Text } from '@react-email/text'; import { Text } from '@react-email/text';
import { Footer } from './components/footer';
type EmailProps = { type EmailProps = {
title: string; title: string;
@@ -11,33 +12,31 @@ type EmailProps = {
export default function Email({ title, body }: EmailProps) { export default function Email({ title, body }: EmailProps) {
return ( return (
<Html> <Html>
<Section style={main}> <Section
className='mx-auto w-full max-w-2xl overflow-hidden rounded-lg bg-white shadow-lg'
style={main}
>
<Container style={container}> <Container style={container}>
<Text style={heading}>{title}</Text> <h1 className='mt-4 text-center text-3xl font-bold'>{title}</h1>
<Text style={paragraph}>{body}</Text> <Text style={paragraph}>{body}</Text>
</Container> </Container>
<Footer />
</Section> </Section>
</Html> </Html>
); );
} }
const main = { const main = {
backgroundColor: '#ffffff', backgroundColor: '#ffffff'
}; };
const container = { const container = {
margin: '0 auto', margin: '0 auto',
padding: '20px 0 48px', padding: '20px 0 48px',
width: '580px', width: '580px'
};
const heading = {
fontSize: '24px',
fontWeight: 'bold',
marginBottom: '16px',
}; };
const paragraph = { const paragraph = {
fontSize: '16px', fontSize: '16px',
marginBottom: '16px', marginBottom: '16px'
}; };

View File

@@ -1,3 +1,5 @@
import { Link } from '../custom/link';
import { Note } from './components/note';
import Email from './template'; import Email from './template';
export default function UnsubscribeTemplate() { export default function UnsubscribeTemplate() {
@@ -6,8 +8,26 @@ export default function UnsubscribeTemplate() {
template: ( template: (
<Email <Email
title="We're sad you're leaving :(" title="We're sad you're leaving :("
body={<>You have unsubscribed from the newsletter.</>} body={
<div className='mt-8'>
<p className='mt-2 text-base text-gray-700 dark:text-gray-400'>
You have been successfully unsubscribed from our newsletter. You
won&apos;t receive any further communications from us unless you
explicitly opt-in again.
</p>
<div className='my-8 flex justify-center'>
<Link path='/' text='Re-subscribe' />
</div>
<Note>
If you have any questions or concerns, please feel free to{' '}
<a className='text-blue-500 underline' href='#'>
contact us
</a>
.
</Note>
</div>
}
/> />
), )
}; };
} }