style: general tweaking

This commit is contained in:
Riccardo
2023-12-18 18:17:50 +01:00
parent 37ffb951a9
commit 5407e2cf79
10 changed files with 99 additions and 115 deletions

View File

@@ -29,3 +29,5 @@ RESEND_FROM=""
SECRET_HASH="" SECRET_HASH=""
HOME_URL="" HOME_URL=""
MAINTENANCE_MODE=0 MAINTENANCE_MODE=0
BRAND_NAME=""
BRAND_EMAIL=""

View File

@@ -1,5 +1,11 @@
# Hackernews newsletter # Hackernews newsletter
## Next up
- Batch email (Resend: ETA early 2024)
- Custom url shortener for links in the newsletter
- Cron every 10 minutes: people are more likely to open the newsletter if delivered around the time when they subscribed (if cron becomes not enough, then the cost of sending all the emails might be a bigger issue)
## Vercel basics ## Vercel basics
Install vercel cli Install vercel cli

View File

@@ -6,10 +6,10 @@ export async function GET() {
orderBy: { orderBy: {
createdAt: 'desc' createdAt: 'desc'
}, },
take: 50 take: 100
}); });
if (news && news.length === 50) { if (news && news.length === 100) {
return ApiResponse(200, JSON.stringify(news)); return ApiResponse(200, JSON.stringify(news));
} }

View File

@@ -1,5 +1,6 @@
import { useState } from 'react'; import { useState } from 'react';
import { z } from 'zod'; import { z } from 'zod';
import { getRandomColor } from '../../../../utils/getRandomColor';
import { NewsSchema } from '../../../../utils/schemas'; import { NewsSchema } from '../../../../utils/schemas';
type CardContentProps = { type CardContentProps = {
@@ -7,15 +8,6 @@ type CardContentProps = {
side: boolean; side: boolean;
}; };
function getRandomColor() {
const letters = '456789ABCDEF';
let color = '#';
for (let i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 12)];
}
return color;
}
export function TileContent({ story, side }: CardContentProps) { export function TileContent({ story, side }: CardContentProps) {
const [firstColor, setFirstColor] = useState(getRandomColor()); const [firstColor, setFirstColor] = useState(getRandomColor());
const [secondColor, setSecondColor] = useState(getRandomColor()); const [secondColor, setSecondColor] = useState(getRandomColor());
@@ -32,14 +24,15 @@ export function TileContent({ story, side }: CardContentProps) {
return ( return (
<div <div
className={`h-40 w-40 overflow-hidden p-6 shadow-sm`} className={`h-40 w-40 overflow-hidden rounded-lg p-6 shadow-sm`}
style={{ style={{
backgroundColor: `${color}` backgroundColor: `${color}`
}} }}
> >
<h1 className='overflow-auto font-semibold'>{story.title}</h1> <h1 className='overflow-auto font-semibold'>{story.title}</h1>
<p className='overflow-auto italic'>{story.by}</p> <p className='overflow-auto italic'>by {story.by}</p>
<div <div
className='rounded-lg'
style={{ style={{
position: 'absolute', position: 'absolute',
left: 0, left: 0,

View File

@@ -1,14 +1,22 @@
import { getRandomColor } from '../../../utils/getRandomColor';
export function Footer() { export function Footer() {
const background = getRandomColor();
return ( return (
<footer className='mt-8 border-t border-gray-200 pt-6'> <footer
<div className='mb-4 ml-8 flex items-center justify-between'> className='mt-8 bg-blue-200 pt-6 text-black'
style={{ backgroundColor: `${background}` }}
>
<div className='ml-8 flex items-center justify-between pb-4'>
<div> <div>
<h4 className='text-lg font-semibold text-gray-700'>Contact Us</h4> <h4 className='text-lg font-semibold'>Contact Us</h4>
<p className='text-gray-600'>FromPixels</p> <p>{process.env.BRAND_NAME}</p>
<p className='text-gray-600'>Email: info@frompixels.com</p> <a href={`mailto:${process.env.BRAND_EMAIL}`}>
Email: {process.env.BRAND_EMAIL}
</a>
</div> </div>
</div> </div>
<p className='text-sm text-gray-500'></p>
</footer> </footer>
); );
} }

View File

@@ -9,15 +9,13 @@ export default function ConfirmationTemplate(code: string) {
<Email <Email
title={'Welcome!'} title={'Welcome!'}
body={ body={
<div className='mt-8'> <div className='text-base text-gray-700 dark:text-gray-400'>
<p className='text-base text-gray-700 dark:text-gray-400'> <p>Dear subscriber,</p>
Dear subscriber, <p className='mt-2 '>
</p>
<p className='mt-2 text-base text-gray-700 dark:text-gray-400'>
thank you for subscribing to our newsletter! Please click the thank you for subscribing to our newsletter! Please click the
button below to confirm your subscription. button below to confirm your subscription.
</p> </p>
<div className='mt-8 flex justify-center'> <div className='my-8 flex justify-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'

View File

@@ -1,10 +1,7 @@
import { Container } from '@react-email/container';
import { Html } from '@react-email/html';
import { Section } from '@react-email/section';
import { Text } from '@react-email/text';
import { z } from 'zod'; import { z } from 'zod';
import { getRandomColor } from '../../utils/getRandomColor';
import { NewsSchema } from '../../utils/schemas'; import { NewsSchema } from '../../utils/schemas';
import { Footer } from './components/footer'; import Email from './template';
export default function NewsletterTemplate( export default function NewsletterTemplate(
stories: z.infer<typeof NewsSchema>[] stories: z.infer<typeof NewsSchema>[]
@@ -26,36 +23,27 @@ export default function NewsletterTemplate(
return { return {
subject: `What's new from Hackernews?`, subject: `What's new from Hackernews?`,
template: ( template: (
<Html> <Email
<Section className='bg-white'> title='Good day!'
<div className='mx-auto w-full max-w-2xl overflow-hidden rounded-lg bg-white shadow-lg'> body={
<div className='text-center '> <div className='text-base text-gray-700 dark:text-gray-400'>
<h1 className='my-4 text-3xl font-bold'>Good day!</h1> <p className='flex justify-center'>
<p>
Here is something{' '} Here is something{' '}
{sayings[Math.floor(Math.random() * sayings.length)]}: {sayings[Math.floor(Math.random() * sayings.length)]}:
</p> </p>
</div>
<Container
style={{
margin: '0 auto',
padding: '20px 0 48px',
width: '580px'
}}
>
<Text>
{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' className='mt-8 rounded-lg border bg-card text-card-foreground shadow-sm'
data-v0-t='card' data-v0-t='card'
style={{ backgroundColor: `${background}` }}
> >
<div className='flex flex-col space-y-1.5 p-6'> <div className='flex flex-col space-y-1.5 px-6 pb-2 pt-6'>
<h2 className='text-2xl font-semibold'> <h2 className='text-2xl font-semibold'>{story.title}</h2>
{story.title} <p className='italic'>by {story.by}</p>
</h2>
<p>{story.by}</p>
</div> </div>
{story.text && ( {story.text && (
<div className='px-6'> <div className='px-6'>
@@ -71,26 +59,16 @@ export default function NewsletterTemplate(
</div> </div>
)} )}
{story.url && ( {story.url && (
<div className='p-4 text-right'> <div className='p-6 text-right font-bold'>
<p> <a href={story.url}>Read more</a>
<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>
)} )}
</div> </div>
); );
})} })}
</Text>
</Container>
<Footer />
</div> </div>
</Section> }
</Html> />
) )
}; };
} }

View File

@@ -1,7 +1,6 @@
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 { getRandomColor } from '../../utils/getRandomColor';
import { Footer } from './components/footer'; import { Footer } from './components/footer';
type EmailProps = { type EmailProps = {
@@ -10,26 +9,18 @@ type EmailProps = {
}; };
export default function Email({ title, body }: EmailProps) { export default function Email({ title, body }: EmailProps) {
const titleBackground = getRandomColor();
return ( return (
<Html> <Html>
<Section className='mx-auto w-full max-w-2xl overflow-hidden rounded-lg bg-white shadow-lg'> <Section className='max-w-2xl overflow-hidden rounded-lg bg-white shadow-lg'>
<Container <h1
style={{ className='p-8 text-center text-3xl font-bold text-black'
margin: '0 auto', style={{ backgroundColor: `${titleBackground}` }}
padding: '20px 0 48px',
width: '580px'
}}
> >
<h1 className='mt-4 text-center text-3xl font-bold'>{title}</h1> {title}
<Text </h1>
style={{ <div className='m-8 p-8'>{body}</div>
fontSize: '16px',
marginBottom: '16px'
}}
>
{body}
</Text>
</Container>
<Footer /> <Footer />
</Section> </Section>
</Html> </Html>

View File

@@ -9,8 +9,8 @@ export default function UnsubscribeTemplate() {
<Email <Email
title="We're sad you're leaving :(" title="We're sad you're leaving :("
body={ body={
<div className='mt-8'> <div className='text-base text-gray-700 dark:text-gray-400'>
<p className='mt-2 text-base text-gray-700 dark:text-gray-400'> <p className='mt-2 '>
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.

8
utils/getRandomColor.ts Normal file
View File

@@ -0,0 +1,8 @@
export function getRandomColor() {
const letters = '6789ABCDEF';
let color = '#';
for (let i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 10)];
}
return color;
}