Skip to content

Commit

Permalink
Merge pull request #1606 from ErickCReis/feat/react-email
Browse files Browse the repository at this point in the history
React-email e componentização de emails
  • Loading branch information
aprendendofelipe authored Feb 10, 2024
2 parents d0c7e96 + 872e884 commit 61f40e7
Show file tree
Hide file tree
Showing 22 changed files with 15,287 additions and 8,318 deletions.
10 changes: 10 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,16 @@ Created migration -- ./infra/migrations/1655399502254_alter-table-users-add-tabc

Caso esta nova migração esteja válida, ela será automaticamente executada na próxima vez que você rodar o comando `npm run dev`. Caso contrário, o serviço não irá subir e os logs de erro estarão registrados no arquivo `migrations.log` encontrado na raiz do projeto.

### Templates de email

Os templates de email estão localizados em `models/transactional/emails`, eles utilizam o [react-email](https://react.email/) para a composição do layout e renderização.

Para visualizar e testar os templates, você pode utilizar o comando:

```bash
npm run email
```

### Commit das alterações

Após finalizar suas alterações e se certificar que todos os testes estão passando com o comando geral `npm test`, chegou a hora de fazer o commit das suas alterações.
Expand Down
3 changes: 2 additions & 1 deletion infra/email.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ if (!webserver.isServerlessRuntime) {

const transporter = nodemailer.createTransport(transporterConfiguration);

async function send({ from, to, subject, text }) {
async function send({ from, to, subject, html, text }) {
const mailOptions = {
from: from,
to: to,
subject: subject,
html: html,
text: text,
};

Expand Down
17 changes: 8 additions & 9 deletions models/activation.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import database from 'infra/database.js';
import email from 'infra/email.js';
import webserver from 'infra/webserver.js';
import authorization from 'models/authorization.js';
import { ActivationEmail } from 'models/transactional';
import user from 'models/user.js';

async function createAndSendActivationEmail(user) {
Expand All @@ -24,22 +25,20 @@ async function create(user) {
async function sendEmailToUser(user, tokenId) {
const activationPageEndpoint = getActivationPageEndpoint(tokenId);

const { html, text } = ActivationEmail({
username: user.username,
activationLink: activationPageEndpoint,
});

await email.send({
from: {
name: 'TabNews',
address: '[email protected]',
},
to: user.email,
subject: 'Ative seu cadastro no TabNews',
text: `${user.username}, clique no link abaixo para ativar seu cadastro no TabNews:
${activationPageEndpoint}
Caso você não tenha feito esta requisição, ignore esse email.
Atenciosamente,
Equipe TabNews
Rua Antônio da Veiga, 495, Blumenau, SC, 89012-500`,
html,
text,
});
}

Expand Down
17 changes: 8 additions & 9 deletions models/email-confirmation.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { NotFoundError } from 'errors';
import database from 'infra/database.js';
import email from 'infra/email.js';
import webserver from 'infra/webserver.js';
import { ConfirmationEmail } from 'models/transactional';
import user from 'models/user.js';

async function createAndSendEmail(userId, newEmail, options) {
Expand Down Expand Up @@ -32,22 +33,20 @@ async function create(userId, newEmail, options) {
async function sendEmailToUser(user, newEmail, tokenId) {
const emailConfirmationPageEndpoint = getEmailConfirmationPageEndpoint(tokenId);

const { html, text } = ConfirmationEmail({
username: user.username,
confirmationLink: emailConfirmationPageEndpoint,
});

await email.send({
from: {
name: 'TabNews',
address: '[email protected]',
},
to: newEmail,
subject: 'Confirme seu novo email',
text: `${user.username}, uma alteração de email foi solicitada.
Clique no link abaixo para confirmar esta alteração:
${emailConfirmationPageEndpoint}
Atenciosamente,
Equipe TabNews
Rua Antônio da Veiga, 495, Blumenau, SC, 89012-500`,
html,
text,
});
}

Expand Down
22 changes: 11 additions & 11 deletions models/notification.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import email from 'infra/email.js';
import webserver from 'infra/webserver.js';
import authorization from 'models/authorization.js';
import content from 'models/content.js';
import { NotificationEmail } from 'models/transactional';
import user from 'models/user.js';

async function sendReplyEmailToParentUser(createdContent) {
Expand Down Expand Up @@ -43,22 +44,21 @@ async function sendReplyEmailToParentUser(createdContent) {
rootContent: secureRootContent,
});

const { html, text } = NotificationEmail({
username: parentContentUser.username,
bodyReplyLine: bodyReplyLine,
contentLink: childContendUrl,
});

await email.send({
to: parentContentUser.email,
from: {
name: 'TabNews',
address: '[email protected]',
},
subject: subject,
text: `Olá, ${parentContentUser.username}!
${bodyReplyLine}
${childContendUrl}
Atenciosamente,
Equipe TabNews
Rua Antônio da Veiga, 495, Blumenau, SC, 89012-500`,
html,
text,
});
}
}
Expand All @@ -72,10 +72,10 @@ function getSubject({ createdContent, rootContent }) {

function getBodyReplyLine({ createdContent, rootContent }) {
if (createdContent.parent_id === rootContent.id) {
return `"${createdContent.owner_username}" respondeu à sua publicação "${rootContent.title}". Para ler a resposta, utilize o link abaixo:`;
return `"${createdContent.owner_username}" respondeu à sua publicação "${rootContent.title}".`;
}

return `"${createdContent.owner_username}" respondeu ao seu comentário na publicação "${rootContent.title}". Para ler a resposta, utilize o link abaixo:`;
return `"${createdContent.owner_username}" respondeu ao seu comentário na publicação "${rootContent.title}".`;
}

function getChildContendUrl({ owner_username, slug }) {
Expand Down
17 changes: 8 additions & 9 deletions models/recovery.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import database from 'infra/database.js';
import email from 'infra/email.js';
import webserver from 'infra/webserver.js';
import session from 'models/session';
import { RecoveryEmail } from 'models/transactional';
import user from 'models/user.js';

async function createAndSendRecoveryEmail(secureInputValues) {
Expand Down Expand Up @@ -49,22 +50,20 @@ async function create(user) {
async function sendEmailToUser(user, tokenId) {
const recoverPageEndpoint = getRecoverPageEndpoint(tokenId);

const { html, text } = RecoveryEmail({
username: user.username,
recoveryLink: recoverPageEndpoint,
});

await email.send({
from: {
name: 'TabNews',
address: '[email protected]',
},
to: user.email,
subject: 'Recuperação de Senha',
text: `${user.username}, foi solicitada uma recuperação de senha. Caso você não tenha feito a solicitação, ignore esse email.
Caso você tenha feito essa solicitação, clique no link abaixo para definir uma nova senha:
${recoverPageEndpoint}
Atenciosamente,
Equipe TabNews
Rua Antônio da Veiga, 495, Blumenau, SC, 89012-500`,
html,
text,
});
}

Expand Down
60 changes: 60 additions & 0 deletions models/transactional/components/default-layout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { Body, Container, Head, Heading, Html, Img, Preview, Text } from '@react-email/components';

export const DefaultLayoutText = ({ username, content }) => {
return `Olá, ${username}!
${content}
Atenciosamente,
Equipe TabNews
Rua Antônio da Veiga, 495, Blumenau, SC, 89012-500`;
};

export const DefaultLayout = ({ username, previewText, children }) => (
<Html>
<Head />
<Preview>{previewText}</Preview>
<Body style={main}>
<Container style={container}>
<Heading style={h1}>Olá, {username}!</Heading>

{children}

<Text style={footer}>
Atenciosamente, <br />
Equipe TabNews <br />
Rua Antônio da Veiga, 495, Blumenau, SC, 89012-500
</Text>
<Img src="https://www.tabnews.com.br/favicon.png" width="32" height="32" alt="TabNews" />
</Container>
</Body>
</Html>
);

const main = {
backgroundColor: '#ffffff',
fontFamily:
"-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif",
};

const container = {
paddingLeft: '12px',
paddingRight: '12px',
margin: '0 auto',
};

const h1 = {
color: '#333',
fontSize: '24px',
fontWeight: 'bold',
margin: '40px 0',
padding: '0',
};

const footer = {
color: '#898989',
fontSize: '12px',
lineHeight: '22px',
marginTop: '12px',
marginBottom: '4px',
};
58 changes: 58 additions & 0 deletions models/transactional/emails/activation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { Link, Text } from '@react-email/components';

import { DefaultLayout, DefaultLayoutText } from '../components/default-layout';

export const ActivationEmailText = ({ username, activationLink }) => {
const content = `Clique no link abaixo para ativar seu cadastro no TabNews:
${activationLink}
Caso você não tenha feito esta requisição, ignore esse email.`;

return DefaultLayoutText({ username, content });
};

export const ActivationEmailHtml = ({ username, activationLink }) => (
<DefaultLayout username={username} previewText="Ative seu cadastro no TabNews">
<Link href={activationLink} style={link}>
Clique aqui para ativar seu cadastro no TabNews.
</Link>

<Text style={text}>Se você não conseguir clicar no link, copie e cole o endereço abaixo no seu navegador:</Text>

<code style={code}>{activationLink}</code>

<Text style={text}>Caso você não tenha feito esta requisição, ignore esse email.</Text>
</DefaultLayout>
);

ActivationEmailHtml.PreviewProps = {
username: 'User',
activationLink: 'https://tabnews.com.br/cadastro/ativar/TOKEN_ID',
};

export default ActivationEmailHtml;

const link = {
color: '#2754C5',
fontSize: '14px',
textDecoration: 'underline',
display: 'block',
marginBottom: '16px',
};

const text = {
color: '#333',
fontSize: '14px',
margin: '24px 0',
};

const code = {
backgroundColor: '#f3f3f3',
color: '#333',
display: 'block',
fontSize: '14px',
padding: '12px',
borderRadius: '8px',
wordBreak: 'break-all',
};
62 changes: 62 additions & 0 deletions models/transactional/emails/confirmation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { Link, Text } from '@react-email/components';

import { DefaultLayout, DefaultLayoutText } from '../components/default-layout';

export const ConfirmationEmailText = ({ username, confirmationLink }) => {
const content = `Uma alteração de email foi solicitada.
Clique no link abaixo para confirmar esta alteração:
${confirmationLink}
Caso você não tenha feito esta requisição, ignore esse email.`;

return DefaultLayoutText({ username, content });
};

export const ConfirmationEmailHtml = ({ username, confirmationLink }) => (
<DefaultLayout username={username} previewText="Alteração de email no Tabnews">
<Text style={text}>Uma alteração de email foi solicitada.</Text>

<Link href={confirmationLink} style={link}>
Clique aqui para confirmar esta alteração.
</Link>

<Text style={text}>Se você não conseguir clicar no link, copie e cole o endereço abaixo no seu navegador:</Text>

<code style={code}>{confirmationLink}</code>

<Text style={text}>Caso você não tenha feito esta requisição, ignore esse email.</Text>
</DefaultLayout>
);

ConfirmationEmailHtml.PreviewProps = {
username: 'User',
confirmationLink: 'https://tabnews.com.br/perfil/confirmar-email/TOKEN_ID',
};

export default ConfirmationEmailHtml;

const link = {
color: '#2754C5',
fontSize: '14px',
textDecoration: 'underline',
display: 'block',
marginBottom: '16px',
};

const text = {
color: '#333',
fontSize: '14px',
margin: '24px 0',
};

const code = {
backgroundColor: '#f3f3f3',
color: '#333',
display: 'block',
fontSize: '14px',
padding: '12px',
borderRadius: '8px',
wordBreak: 'break-all',
};
Loading

0 comments on commit 61f40e7

Please sign in to comment.