style: email tweaking

This commit is contained in:
Riccardo
2023-12-19 20:04:32 +01:00
parent c0d6b73aca
commit 3af079a0f5
17 changed files with 155 additions and 110 deletions

View File

@@ -47,7 +47,11 @@ export async function GET(request: Request) {
createdAt: { createdAt: {
gt: new Date(Date.now() - 1000 * 60 * 60 * 24) gt: new Date(Date.now() - 1000 * 60 * 60 * 24)
} }
} },
orderBy: {
score: 'desc'
},
take: 25
}); });
const validRankedNews = news const validRankedNews = news

View File

@@ -2,7 +2,7 @@ import { Analytics } from '@vercel/analytics/react';
import type { Metadata } from 'next'; import type { Metadata } from 'next';
import { Inter as FontSans } from 'next/font/google'; import { Inter as FontSans } from 'next/font/google';
import { Background } from '../components/custom/background/background'; import { Background } from '../components/custom/background/background';
import { cn } from '../utils/utils'; import { cn } from '../utils/ui';
import './globals.css'; import './globals.css';
export const metadata: Metadata = { export const metadata: Metadata = {

View File

@@ -1,21 +1,30 @@
import { getRandomColor } from '../../../utils/getRandomColor';
export function Footer() { export function Footer() {
const background = getRandomColor();
return ( return (
<footer <footer
className='mt-8 bg-blue-200 pt-6 text-black' style={{
style={{ backgroundColor: `${background}` }} paddingTop: '1.5rem',
backgroundColor: `#E6F2F2`,
color: 'black'
}}
>
<div
style={{
marginLeft: '2rem',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
paddingBottom: '2rem'
}}
> >
<div className='ml-8 flex items-center justify-between pb-4'>
<div> <div>
<h4 className='text-lg font-semibold'>Contact Us</h4> <h4 style={{ fontSize: '1.125rem', fontWeight: '600' }}>
Contact Us
</h4>
<p>{process.env.NEXT_PUBLIC_BRAND_NAME}</p> <p>{process.env.NEXT_PUBLIC_BRAND_NAME}</p>
<p> <p>
Email:{' '} Email:{' '}
<a href={`mailto:${process.env.NEXT_PUBLIC_BRAND_EMAIL}`}> <a href={`mailto:${process.env.NEXT_PUBLIC_BRAND_EMAIL}`}>
Email: {process.env.NEXT_PUBLIC_BRAND_EMAIL} {process.env.NEXT_PUBLIC_BRAND_EMAIL}
</a> </a>
</p> </p>
</div> </div>

View File

@@ -4,8 +4,15 @@ type NoteProps = {
export function Note({ children }: NoteProps) { export function Note({ children }: NoteProps) {
return ( return (
<div className='mt-8 bg-gray-100 px-6 py-4 dark:bg-gray-800'> <div
<div className='text-sm text-gray-600 dark:text-gray-400'>{children}</div> style={{
marginTop: '2rem',
padding: '1.5rem 1.5rem',
backgroundColor: '#EBF1F5',
color: '#718096'
}}
>
<div style={{ fontSize: '0.875rem' }}>{children}</div>
</div> </div>
); );
} }

View File

@@ -1,24 +1,30 @@
import { Link } from '../custom/link'; import { Link } from '../custom/link';
import { Note } from './components/note'; import { Note } from './components/note';
import Email from './template'; import Template from './template';
export default function ConfirmationTemplate(code: string) { export default function ConfirmationTemplate(code: string) {
return { return {
subject: 'Welcome!', subject: 'Welcome!',
template: ( template: (
<Email <Template
title={'Welcome!'} title='Welcome!'
body={ body={
<div className='text-base text-gray-700 dark:text-gray-400'> <div style={{ fontSize: '1rem', color: '#4a5568' }}>
<p>Dear subscriber,</p> <p>Dear subscriber,</p>
<p className='mt-2 '> <p style={{ marginTop: '0.5rem' }}>
thank you for subscribing to our newsletter! Please click the thank you for subscribing to our newsletter! Please click the link
button below to confirm your subscription. below to confirm your subscription.
</p> </p>
<div className='my-8 flex justify-center'> <div
style={{
margin: '2rem 0',
display: 'flex',
justifyContent: 'center'
}}
>
<Link <Link
path={`${process.env.HOME_URL}/confirmation?code=${code}`} path={`${process.env.HOME_URL}/confirmation?code=${code}`}
text='Confirm Subscription' text='Confirm subscription'
/> />
</div> </div>
<Note> <Note>

View File

@@ -0,0 +1,13 @@
export 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'
];

View File

@@ -1,54 +1,61 @@
import { z } from 'zod'; import { z } from 'zod';
import { getRandomColor } from '../../utils/getRandomColor';
import { NewsSchema } from '../../utils/schemas'; import { NewsSchema } from '../../utils/schemas';
import Email from './template'; import { sayings } from './helpers/newsletterSayings';
import Template from './template';
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 the Hackernews forum?`,
template: ( template: (
<Email <Template
title='Good day!' title={`Here is something
${sayings[Math.floor(Math.random() * sayings.length)]}!`}
body={ body={
<div className='text-base text-gray-700 dark:text-gray-400'> <div>
<p className='flex justify-center'>
Here is something{' '}
{sayings[Math.floor(Math.random() * sayings.length)]}:
</p>
{stories.map(story => { {stories.map(story => {
const background = getRandomColor();
return ( return (
<div <div
key={story.id} key={story.id}
className='mt-8 rounded-lg border bg-card text-card-foreground shadow-sm' style={{
marginTop: '2rem',
marginBottom: '2rem',
borderRadius: '0.5rem',
border: '1px solid #e5e7eb',
backgroundColor: `white`,
color: '#111827',
boxShadow: '0 16px 32px 0 rgba(0, 0, 0, 0.05)'
}}
data-v0-t='card' data-v0-t='card'
style={{ backgroundColor: `${background}` }}
> >
<div className='flex flex-col space-y-1.5 px-6 pb-2 pt-6'> <div
<h2 className='text-2xl font-semibold'>{story.title}</h2> style={{
<p className='italic'>by {story.by}</p> display: 'flex',
flexDirection: 'column',
gap: '0.375rem',
paddingTop: '1.5rem',
paddingLeft: '1.5rem',
paddingRight: '1.5rem'
}}
>
<h2 style={{ fontSize: '1.5rem', fontWeight: '600' }}>
{story.title}
</h2>
<p style={{ fontSize: '1rem', fontStyle: 'italic' }}>
by {story.by}
</p>
</div> </div>
{story.text && ( {story.text && (
<div className='px-6'> <div
style={{
paddingLeft: '1.5rem',
fontSize: '1rem',
paddingRight: '1.5rem'
}}
>
<p <p
className='mb-4'
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: __html:
story.text.length > 500 story.text.length > 500
@@ -59,7 +66,15 @@ export default function NewsletterTemplate(
</div> </div>
)} )}
{story.url && ( {story.url && (
<div className='p-6 text-right font-bold'> <div
style={{
paddingBottom: '1.5rem',
paddingLeft: '1.5rem',
paddingRight: '1.5rem',
textAlign: 'right',
fontWeight: 'bold'
}}
>
<a href={story.url}>Read more</a> <a href={story.url}>Read more</a>
</div> </div>
)} )}

View File

@@ -1,28 +1,34 @@
import { Html } from '@react-email/html';
import { Section } from '@react-email/section';
import { getRandomColor } from '../../utils/getRandomColor';
import { Footer } from './components/footer'; import { Footer } from './components/footer';
type EmailProps = { type TemplateProps = {
title: string; title: string;
body: JSX.Element; body: JSX.Element;
}; };
export default function Email({ title, body }: EmailProps) { export default function Template({ title, body }: TemplateProps) {
const titleBackground = getRandomColor();
return ( return (
<Html> <div
<Section className='max-w-2xl overflow-hidden rounded-lg bg-white shadow-lg'> style={{
maxWidth: '720px',
alignContent: 'center',
alignItems: 'center',
backgroundColor: '#F9FBFB',
}}
>
<h1 <h1
className='p-8 text-center text-3xl font-bold text-black' style={{
style={{ backgroundColor: `${titleBackground}` }} padding: '20px',
textAlign: 'center',
fontSize: '24px',
fontWeight: 'bold',
color: 'white',
backgroundColor: `#8230CC`,
}}
> >
{title} {title}
</h1> </h1>
<div className='m-8 p-8'>{body}</div> <div style={{ margin: '20px', padding: '20px' }}>{body}</div>
<Footer /> <Footer />
</Section> </div>
</Html>
); );
} }

View File

@@ -1,26 +1,35 @@
import { Link } from '../custom/link'; import { Link } from '../custom/link';
import { Note } from './components/note'; import { Note } from './components/note';
import Email from './template'; import Template from './template';
export default function UnsubscribeTemplate() { export default function UnsubscribeTemplate() {
return { return {
subject: 'Unsubscribe confirmation', subject: 'Unsubscribe confirmation',
template: ( template: (
<Email <Template
title="We're sad you're leaving :(" title="We're sad to see you go :("
body={ body={
<div className='text-base text-gray-700 dark:text-gray-400'> <div style={{ fontSize: '1rem', color: '#4a5568' }}>
<p className='mt-2 '> <p style={{ marginTop: '0.5rem' }}>
You have been successfully unsubscribed from our newsletter. You You have been successfully unsubscribed from our newsletter. You
won&apos;t receive any further communications from us unless you won&apos;t receive any further communications from us unless you
explicitly opt-in again. explicitly opt-in again.
</p> </p>
<div className='my-8 flex justify-center'> <div
style={{
margin: '2rem 0',
display: 'flex',
justifyContent: 'center'
}}
>
<Link path='/' text='Re-subscribe' /> <Link path='/' text='Re-subscribe' />
</div> </div>
<Note> <Note>
If you have any questions or concerns, please feel free to{' '} If you have any questions or concerns, please feel free to{' '}
<a className='text-blue-500 underline' href='#'> <a
style={{ color: '#3b82f6', textDecoration: 'underline' }}
href={`mailto:${process.env.NEXT_PUBLIC_BRAND_EMAIL}`}
>
contact us contact us
</a> </a>
. .

View File

@@ -1,7 +1,7 @@
import { Slot } from '@radix-ui/react-slot'; import { Slot } from '@radix-ui/react-slot';
import { cva, type VariantProps } from 'class-variance-authority'; import { cva, type VariantProps } from 'class-variance-authority';
import * as React from 'react'; import * as React from 'react';
import { cn } from '../../utils/utils'; import { cn } from '../../utils/ui';
const buttonVariants = cva( const buttonVariants = cva(
'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50', 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',

View File

@@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import { cn } from '../../utils/utils'; import { cn } from '../../utils/ui';
const Card = React.forwardRef< const Card = React.forwardRef<
HTMLDivElement, HTMLDivElement,

View File

@@ -10,7 +10,7 @@ import {
useFormContext useFormContext
} from 'react-hook-form'; } from 'react-hook-form';
import { Label } from '../../components/ui/label'; import { Label } from '../../components/ui/label';
import { cn } from '../../utils/utils'; import { cn } from '../../utils/ui';
const Form = FormProvider; const Form = FormProvider;

View File

@@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import { cn } from '../../utils/utils'; import { cn } from '../../utils/ui';
const Input = React.forwardRef< const Input = React.forwardRef<
HTMLInputElement, HTMLInputElement,

View File

@@ -3,7 +3,7 @@
import * as LabelPrimitive from '@radix-ui/react-label'; import * as LabelPrimitive from '@radix-ui/react-label';
import { cva, type VariantProps } from 'class-variance-authority'; import { cva, type VariantProps } from 'class-variance-authority';
import * as React from 'react'; import * as React from 'react';
import { cn } from '../../utils/utils'; import { cn } from '../../utils/ui';
const labelVariants = cva( const labelVariants = cva(
'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70' 'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70'

View File

@@ -22,10 +22,6 @@
"@prisma/client": "^5.6.0", "@prisma/client": "^5.6.0",
"@radix-ui/react-label": "^2.0.2", "@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-slot": "^1.0.2",
"@react-email/container": "^0.0.10",
"@react-email/html": "^0.0.6",
"@react-email/section": "^0.0.10",
"@react-email/text": "^0.0.6",
"@typescript-eslint/eslint-plugin": "^6.12.0", "@typescript-eslint/eslint-plugin": "^6.12.0",
"@vercel/analytics": "^1.1.1", "@vercel/analytics": "^1.1.1",
"class-variance-authority": "^0.7.0", "class-variance-authority": "^0.7.0",

View File

@@ -460,16 +460,6 @@
"@babel/runtime" "^7.13.10" "@babel/runtime" "^7.13.10"
"@radix-ui/react-compose-refs" "1.0.1" "@radix-ui/react-compose-refs" "1.0.1"
"@react-email/container@^0.0.10":
version "0.0.10"
resolved "https://registry.yarnpkg.com/@react-email/container/-/container-0.0.10.tgz#1f9e883d77cb1b1cbd3cbbe271a545ed1ce622be"
integrity sha512-goishY7ocq+lord0043/LZK268bqvMFW/sxpUt/dSCPJyrrZZNCbpW2t8w8HztU38cYj0qGQLxO5Qvpn/RER3w==
"@react-email/html@^0.0.6":
version "0.0.6"
resolved "https://registry.yarnpkg.com/@react-email/html/-/html-0.0.6.tgz#a54b51849b21ec131c491b16f7695867bbe7ac81"
integrity sha512-8Fo20VOqxqc087gGEPjT8uos06fTXIC8NSoiJxpiwAkwiKtQnQH/jOdoLv6XaWh5Zt2clj1uokaoklnaM5rY1w==
"@react-email/render@0.0.9": "@react-email/render@0.0.9":
version "0.0.9" version "0.0.9"
resolved "https://registry.yarnpkg.com/@react-email/render/-/render-0.0.9.tgz#145d3c3fcbc9c4277234e44a58214bb48e8f79bb" resolved "https://registry.yarnpkg.com/@react-email/render/-/render-0.0.9.tgz#145d3c3fcbc9c4277234e44a58214bb48e8f79bb"
@@ -480,16 +470,6 @@
react "18.2.0" react "18.2.0"
react-dom "18.2.0" react-dom "18.2.0"
"@react-email/section@^0.0.10":
version "0.0.10"
resolved "https://registry.yarnpkg.com/@react-email/section/-/section-0.0.10.tgz#69e0d9956f74a804ad6c5077b7a26e1e8c07edcc"
integrity sha512-x9B2KYFqj+d8I1fK9bgeVm/3mLE4Qgn4mm/GbDtcJeSzKU/G7bTb7/3+BMDk9SARPGkg5XAuZm1XgcqQQutt2A==
"@react-email/text@^0.0.6":
version "0.0.6"
resolved "https://registry.yarnpkg.com/@react-email/text/-/text-0.0.6.tgz#c28a2b70f3e17de8e93e3fbdf9a18afff3623a29"
integrity sha512-PDUTAD1PjlzXFOIUrR1zuV2xxguL62yne5YLcn1k+u/dVUyzn6iU/5lFShxCfzuh3QDWCf4+JRNnXN9rmV6jzw==
"@rushstack/eslint-patch@^1.3.3": "@rushstack/eslint-patch@^1.3.3":
version "1.6.1" version "1.6.1"
resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.6.1.tgz#9ab8f811930d7af3e3d549183a50884f9eb83f36" resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.6.1.tgz#9ab8f811930d7af3e3d549183a50884f9eb83f36"