Browse Source

fix: email transaltion

master
Pierre-Louis Guhur 1 year ago
parent
commit
5344f5ede2
  1. 218
      functions/send-invite-email/invite-en.html
  2. 19
      functions/send-invite-email/invite-en.txt
  3. 203
      functions/send-invite-email/invite-fr.html
  4. 17
      functions/send-invite-email/invite-fr.txt
  5. 10
      functions/send-invite-email/invite.html
  6. 123
      functions/send-invite-email/send-invite-email.js
  7. 106
      package-lock.json
  8. 2
      package.json
  9. 2
      pages/new/confirm/[pid].jsx
  10. 2
      pages/new/index.js
  11. 26
      services/api.js

218
functions/send-invite-email/invite-en.html

@ -0,0 +1,218 @@
<!DOCTYPE html>
<html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<style type="text/css">
/* FONTS */
@media screen {
@font-face {
font-family: 'Lato';
font-style: normal;
font-weight: 400;
src: local('Lato Regular'), local('Lato-Regular'), url(https://fonts.gstatic.com/s/lato/v11/qIIYRU-oROkIk8vfvxw6QvesZW2xOQ-xsNqO47m55DA.woff) format('woff');
}
@font-face {
font-family: 'Lato';
font-style: normal;
font-weight: 700;
src: local('Lato Bold'), local('Lato-Bold'), url(https://fonts.gstatic.com/s/lato/v11/qdgUG4U09HnJwhYI-uK18wLUuEpTyoUstqEm5AMlJo4.woff) format('woff');
}
@font-face {
font-family: 'Lato';
font-style: italic;
font-weight: 400;
src: local('Lato Italic'), local('Lato-Italic'), url(https://fonts.gstatic.com/s/lato/v11/RYyZNoeFgb0l7W3Vu1aSWOvvDin1pK8aKteLpeZ5c0A.woff) format('woff');
}
@font-face {
font-family: 'Lato';
font-style: italic;
font-weight: 700;
src: local('Lato Bold Italic'), local('Lato-BoldItalic'), url(https://fonts.gstatic.com/s/lato/v11/HkF_qI1x_noxlxhrhMQYELO3LdcAZYWl9Si6vvxL-qU.woff) format('woff');
}
}
/* CLIENT-SPECIFIC STYLES */
body, table, th, a { -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; }
img { -ms-interpolation-mode: bicubic; border: 0; height: auto; line-height: 100%; outline: none; text-decoration: none;}
/* RESET STYLES */
table { border-collapse: collapse !important; padding: 0 !important;}
body { height: 100% !important; margin: 0 !important; padding: 0 !important; width: 100% !important; }
/* iOS BLUE LINKS */
a[x-apple-data-detectors] {
color: inherit !important;
text-decoration: none !important;
font-size: inherit !important;
font-family: inherit !important;
font-weight: inherit !important;
line-height: inherit !important;
}
/* MOBILE STYLES */
@media screen and (max-width:600px){
h1 {
font-size: 32px !important;
line-height: 32px !important;
}
}
/* ANDROID CENTER FIX */
div[style*="margin: 16px 0;"] { margin: 0 !important; }
</style>
</head>
<body style="background-color: #f4f4f4; margin: 0 !important; padding: 0 !important;">
<!-- HIDDEN PREHEADER TEXT -->
<div style="display: none; font-size: 1px; color: #fefefe; line-height: 1px; font-family: 'Lato', Helvetica, Arial, sans-serif; max-height: 0px; max-width: 0px; opacity: 0; overflow: hidden;">
{{#i18n 'email.happy' }}We are happy to send you this email! You will be able to vote using majority judgment.{{/i18n}}
</div>
<table border="0" style="margin: 0px auto 0px auto; width: 100%;" aria-describedby="Email">
<!-- LOGO -->
<tr>
<th scope="col" style="background-color:#efefff ;">
<table border="0" style="margin: 0px auto 0px auto; width: 100%; max-width: 600px;" aria-describedby="Logo picture">
<tr>
<th scope="col" style="vertical-align: top; padding: 40px 10px 40px 10px;">
<a href="https://mieuxvoter.fr/" target="_blank" rel="noopener noreferrer">
<img alt="Logo" src="https://mieuxvoter.fr/wp-content/uploads/2019/10/mieuxvoter_logo.png" width="40" height="40" style="display: block; margin: 0px auto 0px auto; width: 50%; max-width: 250px; min-width: 40px; height: auto; font-family: 'Lato', Helvetica, Arial, sans-serif; color: #ffffff; font-size: 18px;" border="0">
</a>
</th>
</tr>
</table>
</th>
</tr>
<!-- TITLE -->
<tr>
<th scope="col" style="background-color: #efefff; padding: 0px 10px 0px 10px;">
<table border="0" style="margin: 0px auto 0px auto; width: 100%; max-width: 600px;" aria-describedby="email title">
<tr>
<th scope="col" style="vertical-align: top; background-color: #ffffff; padding: 40px 20px 20px 20px; border-radius: 4px 4px 0px 0px; color: #111111; font-family: 'Lato', Helvetica, Arial, sans-serif; font-size: 48px; font-weight: 400; letter-spacing: 4px; line-height: 48px;">
<h1 style="font-size: 48px; font-weight: 400; margin: 0;">{{#i18n 'email.hello'}}Hi, there! 🙂{{/i18n}}</h1>
</th>
</tr>
</table>
</th>
</tr>
<!-- BLOCKS -->
<tr>
<th scope="col" style="background-color: #2a43a0; padding: 0px 10px 0px 10px;">
<table border="0" style="margin: 0px auto 0px auto; width: 100%; max-width: 600px;" aria-describedby="email body">
<!-- BLOCK SUBTITLE-->
<tr>
<th scope="col" style="background-color: #ffffff; padding: 20px 30px 40px 30px; color: #666666; font-family: 'Lato', Helvetica, Arial, sans-serif; font-size: 18px; font-weight: 400; line-height: 25px;" >
<p style="margin: 0; text-align: left;">
{{#i18n 'email.happy'}}We are happy to send you this email! You will be able to vote using majority judgment.{{/i18n}}
</p>
</th>
</tr>
<!-- BLOCK EXPLANATION-->
<tr>
<th scope="col" style="background-color: #ffffff; padding: 20px 30px 40px 30px; color: #666666; font-family: 'Lato', Helvetica, Arial, sans-serif; font-size: 18px; font-weight: 400; line-height: 25px;" >
<p style="margin: 0; text-align: left;">
{{#i18n 'email.why'}}This email was sent to you because your email address was entered to participate in the vote on the subject:{{/i18n}}
&nbsp;
<strong>{{title}}</strong>
</p>
</th>
</tr>
<!-- BULLETPROOF BUTTON BLUE-->
<tr>
<th scope="col" style="background-color: #ffffff;">
<table border="0" style="margin: 0px auto 0px auto; width: 100%;" aria-describedby="Blue bulletproof button">
<tr>
<th scope="col" style="background-color: #ffffff; padding: 20px 30px 60px 30px;">
<table border="0" style="margin: 0px auto 0px auto; border-collapse: collapse;" aria-describedby="invitation url">
<tr>
<th scope="col" style="border-radius: 3px; background-color: #2a43a0;">
<a href="%recipient.urlVote%" target="_blank" style="font-size: 20px; font-family: Helvetica, Arial, sans-serif; color: #ffffff; text-decoration: none; padding: 15px 25px; border-radius: 2px; border: 1px solid #2a43a0; display: inline-block;">
{{#i18n 'common.vote' }}Vote!{{/i18n}}</a></th>
</tr>
</table>
</th>
</tr>
</table>
</th>
</tr>
<!-- BLOCK DOES NOT WORK -->
<tr>
<th scope="col" style="background-color: #ffffff; padding: 20px 30px 40px 30px; color: #666666; font-family: 'Lato', Helvetica, Arial, sans-serif; font-size: 18px; font-weight: 400; line-height: 25px;" >
<p style="margin: 0; text-align: left;">
{{#i18n 'email.copyLink' }}If that doesn't work, copy and paste the following link into your browser:{{/i18n}}
&nbsp;
<a target="_blank" style="color: #2a43a0;">%recipient.urlVote%</a>
</p>
</th>
</tr>
<!-- BLOCK TEXT RESULT -->
<tr>
<th scope="col" style="background-color: #ffffff; padding: 20px 30px 20px 30px; color: #666666; font-family: 'Lato', Helvetica, Arial, sans-serif; font-size: 18px; font-weight: 400; line-height: 25px;" >
<p style="margin: 0; text-align: left;">
{{#i18n 'email.linkResult' }}The results will be available with the following link when the vote is finished:{{/i18n}}
&nbsp;
<a target="_blank" style="color: #2a43a0;">%recipient.urlResult%</a>
</p>
</th>
</tr>
<!-- BLOCK THANKS -->
<tr>
<th scope="col" style="background-color: #ffffff; padding: 0px 30px 40px 30px; border-radius: 0px 0px 4px 4px; color: #666666; font-family: 'Lato', Helvetica, Arial, sans-serif; font-size: 18px; font-weight: 400; line-height: 25px;" >
<p style="margin: 0; text-align: left;">{{#i18n 'email.bye'}}Good vote{{/i18n}},<br>{{#i18n 'common.mieuxvoter'}}Mieux Voter{{/i18n}}</p>
</th>
</tr>
</table>
</th>
</tr>
<!-- SUPPORT CALLOUT -->
<tr>
<th scope="col" style="background-color: #f4f4f4; padding: 30px 10px 0px 10px;">
<table border="0" style="margin: 0px auto 0px auto; width: 100%; max-width: 600px;" aria-describedby="support callout">
<!-- HEADLINE -->
<tr>
<th scope="col" style="background-color: #7d8ecf; padding: 30px 30px 30px 30px; border-radius: 4px 4px 4px 4px; color: #666666; font-family: 'Lato', Helvetica, Arial, sans-serif; font-size: 18px; font-weight: 400; line-height: 25px;" >
<p style="margin: 0;"><strong>
<a href="https://mieuxvoter.fr/index.php/decouvrir/" target="_blank" style="color: #FFFFFF;" rel="noopener noreferrer">
{{#i18n 'email.aboutjm'}}Need any further information?{{/i18n}}
</a></strong>
</p>
<p style="margin: 0;"> <strong>
<a href="https://mieuxvoter.fr/index.php/decouvrir/" target="_blank" style="color: #111111;" rel="noopener noreferrer">
{{#i18n 'common.helpus'}}Do you want to help us?{{/i18n}}
</a></strong>
</p>
</th>
</tr>
</table>
</th>
</tr>
<!-- FOOTER -->
<tr>
<th scope="col" style="background-color: #f4f4f4; padding: 0px 10px 0px 10px;">
<table border="0" style="margin: 0px auto 0px auto; width: 100%; max-width: 600px;" aria-describedby="footer informations">
<!-- EXPLAIN WHY -->
</br>
<tr>
<th scope="col" style="background-color: #f4f4f4; padding: 0px 30px 30px 30px; color: #666666; font-family: 'Lato', Helvetica, Arial, sans-serif; font-size: 14px; font-weight: 400; line-height: 18px;" >
<p style="margin: 0;">
{{#i18n email.why }}You received this email because someone invited you to vote.{{/i18n}}
</p>
</th>
</tr>
<!-- ADDRESS -->
<tr>
<th scope="col" style="background-color: #f4f4f4; padding: 0px 30px 30px 30px; color: #666666; font-family: 'Lato', Helvetica, Arial, sans-serif; font-size: 14px; font-weight: 400; line-height: 18px;" >
<p style="margin: 0;">{{#i18n mieuxvoter }}Mieux Voter{{/i18n}} - <a "mailto:app@mieuxvoter.fr">app@mieuxvoter.fr</a></p>
</th>
</tr>
</table>
</th>
</tr>
</table>
</body>
</html>

19
functions/send-invite-email/invite-en.txt

@ -0,0 +1,19 @@
{{i18n 'email.hello'}}Hi there! 🙂{{i18n}}
{{i18n 'email.happy'}}We are happy to send you this email! You will be able to vote using majority judgment.{{i18n}}
{{i18n 'email.why'}}This email was sent to you because your email was filled out to participate in the vote on the subject:{{i18n}}
{{ title }}
{{i18n 'email.linkVote' }}The link for the vote is as follows:{{i18n}}
%recipient.urlVote%
{{i18n 'email.linkResult' }}The link that will give you the results when they are available is as follows:{{i18n}}
%recipient.urlResult%
{{i18n 'email.bye'}}Good vote{{i18n}}
{{i18n 'common.mieuxvoter'}}Mieux Voter{{i18n}}

203
functions/send-invite-email/invite-fr.html

@ -0,0 +1,203 @@
<!DOCTYPE html>
<html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<style type="text/css">
/* FONTS */
@media screen {
@font-face {
font-family: 'Lato';
font-style: normal;
font-weight: 400;
src: local('Lato Regular'), local('Lato-Regular'), url(https://fonts.gstatic.com/s/lato/v11/qIIYRU-oROkIk8vfvxw6QvesZW2xOQ-xsNqO47m55DA.woff) format('woff');
}
@font-face {
font-family: 'Lato';
font-style: normal;
font-weight: 700;
src: local('Lato Bold'), local('Lato-Bold'), url(https://fonts.gstatic.com/s/lato/v11/qdgUG4U09HnJwhYI-uK18wLUuEpTyoUstqEm5AMlJo4.woff) format('woff');
}
@font-face {
font-family: 'Lato';
font-style: italic;
font-weight: 400;
src: local('Lato Italic'), local('Lato-Italic'), url(https://fonts.gstatic.com/s/lato/v11/RYyZNoeFgb0l7W3Vu1aSWOvvDin1pK8aKteLpeZ5c0A.woff) format('woff');
}
@font-face {
font-family: 'Lato';
font-style: italic;
font-weight: 700;
src: local('Lato Bold Italic'), local('Lato-BoldItalic'), url(https://fonts.gstatic.com/s/lato/v11/HkF_qI1x_noxlxhrhMQYELO3LdcAZYWl9Si6vvxL-qU.woff) format('woff');
}
}
/* CLIENT-SPECIFIC STYLES */
body, table, th, a { -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; }
img { -ms-interpolation-mode: bicubic; border: 0; height: auto; line-height: 100%; outline: none; text-decoration: none;}
/* RESET STYLES */
table { border-collapse: collapse !important; padding: 0 !important;}
body { height: 100% !important; margin: 0 !important; padding: 0 !important; width: 100% !important; }
/* iOS BLUE LINKS */
a[x-apple-data-detectors] {
color: inherit !important;
text-decoration: none !important;
font-size: inherit !important;
font-family: inherit !important;
font-weight: inherit !important;
line-height: inherit !important;
}
/* MOBILE STYLES */
@media screen and (max-width:600px){
h1 {
font-size: 32px !important;
line-height: 32px !important;
}
}
/* ANDROID CENTER FIX */
div[style*="margin: 16px 0;"] { margin: 0 !important; }
</style>
</head>
<body style="background-color: #f4f4f4; margin: 0 !important; padding: 0 !important;">
<!-- HIDDEN PREHEADER TEXT -->
<div style="display: none; font-size: 1px; color: #fefefe; line-height: 1px; font-family: 'Lato', Helvetica, Arial, sans-serif; max-height: 0px; max-width: 0px; opacity: 0; overflow: hidden;">Nous sommes très heureux de vous partager ce lien de vote ! Vous allez pouvoir voter avec le jugement majoritaire.</div>
<table border="0" style="margin: 0px auto 0px auto; width: 100%;" aria-describedby="Email">
<!-- LOGO -->
<tr>
<th scope="col" style="background-color:#efefff ;">
<table border="0" style="margin: 0px auto 0px auto; width: 100%; max-width: 600px;" aria-describedby="Logo picture">
<tr>
<th scope="col" style="vertical-align: top; padding: 40px 10px 40px 10px;">
<a href="https://mieuxvoter.fr/" target="_blank" rel="noopener noreferrer">
<img alt="Logo" src="https://mieuxvoter.fr/wp-content/uploads/2019/10/mieuxvoter_logo.png" width="40" height="40" style="display: block; margin: 0px auto 0px auto; width: 50%; max-width: 250px; min-width: 40px; height: auto; font-family: 'Lato', Helvetica, Arial, sans-serif; color: #ffffff; font-size: 18px;" border="0">
</a>
</th>
</tr>
</table>
</th>
</tr>
<!-- TITLE -->
<tr>
<th scope="col" style="background-color: #efefff; padding: 0px 10px 0px 10px;">
<table border="0" style="margin: 0px auto 0px auto; width: 100%; max-width: 600px;" aria-describedby="email title">
<tr>
<th scope="col" style="vertical-align: top; background-color: #ffffff; padding: 40px 20px 20px 20px; border-radius: 4px 4px 0px 0px; color: #111111; font-family: 'Lato', Helvetica, Arial, sans-serif; font-size: 48px; font-weight: 400; letter-spacing: 4px; line-height: 48px;">
<h1 style="font-size: 48px; font-weight: 400; margin: 0;">Bonjour ! 🙂</h1>
</th>
</tr>
</table>
</th>
</tr>
<!-- BLOCKS -->
<tr>
<th scope="col" style="background-color: #2a43a0; padding: 0px 10px 0px 10px;">
<table border="0" style="margin: 0px auto 0px auto; width: 100%; max-width: 600px;" aria-describedby="email body">
<!-- BLOCK SUBTITLE-->
<tr>
<th scope="col" style="background-color: #ffffff; padding: 20px 30px 40px 30px; color: #666666; font-family: 'Lato', Helvetica, Arial, sans-serif; font-size: 18px; font-weight: 400; line-height: 25px;" >
<p style="margin: 0; text-align: left;">Nous sommes très heureux de vous partager ce lien de vote ! Vous allez pouvoir voter avec le jugement majoritaire.</p>
</th>
</tr>
<!-- BLOCK EXPLANATION-->
<tr>
<th scope="col" style="background-color: #ffffff; padding: 20px 30px 40px 30px; color: #666666; font-family: 'Lato', Helvetica, Arial, sans-serif; font-size: 18px; font-weight: 400; line-height: 25px;" >
<p style="margin: 0; text-align: left;">Vous avez été invité·e à participer à l'élection suivante : </p>
</th>
</tr>
<!-- BULLETPROOF BUTTON BLUE-->
<tr>
<th scope="col" style="background-color: #ffffff;">
<table border="0" style="margin: 0px auto 0px auto; width: 100%;" aria-describedby="Blue bulletproof button">
<tr>
<th scope="col" style="background-color: #ffffff; padding: 20px 30px 60px 30px;">
<table border="0" style="margin: 0px auto 0px auto; border-collapse: collapse;" aria-describedby="invitation url">
<tr>
<th scope="col" style="border-radius: 3px; background-color: #2a43a0;">
<a href="%recipient.urlVote%" target="_blank" style="font-size: 20px; font-family: Helvetica, Arial, sans-serif; color: #ffffff; text-decoration: none; padding: 15px 25px; border-radius: 2px; border: 1px solid #2a43a0; display: inline-block;">Voter !</a></th>
</tr>
</table>
</th>
</tr>
</table>
</th>
</tr>
<!-- BLOCK DOES NOT WORK -->
<tr>
<th scope="col" style="background-color: #ffffff; padding: 20px 30px 40px 30px; color: #666666; font-family: 'Lato', Helvetica, Arial, sans-serif; font-size: 18px; font-weight: 400; line-height: 25px;" >
<p style="margin: 0; text-align: left;">
Si le lien ne fonctionne pas, vous pouvez le copier et le coller dans la barre de navigation de votre navigateur.
&nbsp;
<a target="_blank" style="color: #2a43a0;">%recipient.urlVote%</a>
</p>
</th>
</tr>
<!-- BLOCK TEXT RESULT -->
<tr>
<th scope="col" style="background-color: #ffffff; padding: 20px 30px 20px 30px; color: #666666; font-family: 'Lato', Helvetica, Arial, sans-serif; font-size: 18px; font-weight: 400; line-height: 25px;" >
<p style="margin: 0; text-align: left;">
A la fin de l'élection, vous pourrez accéder aux résultats en cliquant sur ce lien :
&nbsp;
<a target="_blank" style="color: #2a43a0;">%recipient.urlResult%</a>
</p>
</th>
</tr>
<!-- BLOCK THANKS -->
<tr>
<th scope="col" style="background-color: #ffffff; padding: 0px 30px 40px 30px; border-radius: 0px 0px 4px 4px; color: #666666; font-family: 'Lato', Helvetica, Arial, sans-serif; font-size: 18px; font-weight: 400; line-height: 25px;" >
<p style="margin: 0; text-align: left;">Bon vote,<br>Mieux Voter</p>
</th>
</tr>
</table>
</th>
</tr>
<!-- SUPPORT CALLOUT -->
<tr>
<th scope="col" style="background-color: #f4f4f4; padding: 30px 10px 0px 10px;">
<table border="0" style="margin: 0px auto 0px auto; width: 100%; max-width: 600px;" aria-describedby="support callout">
<!-- HEADLINE -->
<tr>
<th scope="col" style="background-color: #7d8ecf; padding: 30px 30px 30px 30px; border-radius: 4px 4px 4px 4px; color: #666666; font-family: 'Lato', Helvetica, Arial, sans-serif; font-size: 18px; font-weight: 400; line-height: 25px;" >
<p style="margin: 0;"><strong>
<a href="https://mieuxvoter.fr/index.php/decouvrir/" target="_blank" style="color: #FFFFFF;" rel="noopener noreferrer">Besoin de plus d'information</a></strong>
</p>
<p style="margin: 0;"> <strong>
<a href="https://mieuxvoter.fr/index.php/decouvrir/" target="_blank" style="color: #111111;" rel="noopener noreferrer">Vous souhaitez nous aider ?</a></strong>
</p>
</th>
</tr>
</table>
</th>
</tr>
<!-- FOOTER -->
<tr>
<th scope="col" style="background-color: #f4f4f4; padding: 0px 10px 0px 10px;">
<table border="0" style="margin: 0px auto 0px auto; width: 100%; max-width: 600px;" aria-describedby="footer informations">
<!-- EXPLAIN WHY -->
</br>
<tr>
<th scope="col" style="background-color: #f4f4f4; padding: 0px 30px 30px 30px; color: #666666; font-family: 'Lato', Helvetica, Arial, sans-serif; font-size: 14px; font-weight: 400; line-height: 18px;" >
<p style="margin: 0;">Vous avez été invité·e à participer à l'élection suivante</p>
</th>
</tr>
<!-- ADDRESS -->
<tr>
<th scope="col" style="background-color: #f4f4f4; padding: 0px 30px 30px 30px; color: #666666; font-family: 'Lato', Helvetica, Arial, sans-serif; font-size: 14px; font-weight: 400; line-height: 18px;" >
<p style="margin: 0;">Mieux Voter - <a "mailto:app@mieuxvoter.fr">app@mieuxvoter.fr</a></p>
</th>
</tr>
</table>
</th>
</tr>
</table>
</body>
</html>

17
functions/send-invite-email/invite-fr.txt

@ -0,0 +1,17 @@
Bonjour ! 🙂
Vous avez été invité·e à participer à l'élection suivante :
{{ title }}
Le lien pour voter est le suivant :
%recipient.urlVote%
A la fin de l'élection, vous pourrez accéder aux résultats en cliquant sur ce lien :
%recipient.urlResult%
Bon vote ! 🤗
Mieux Voter

10
functions/send-invite-email/invite.html

@ -130,7 +130,9 @@
<th scope="col" style="background-color: #ffffff; padding: 20px 30px 60px 30px;">
<table border="0" style="margin: 0px auto 0px auto; border-collapse: collapse;" aria-describedby="invitation url">
<tr>
<th scope="col" style="border-radius: 3px; background-color: #2a43a0;"><a href="{{invitation_url}}" target="_blank" style="font-size: 20px; font-family: Helvetica, Arial, sans-serif; color: #ffffff; text-decoration: none; padding: 15px 25px; border-radius: 2px; border: 1px solid #2a43a0; display: inline-block;">{{#i18n 'common.vote' }}Vote!{{/i18n}}</a></th>
<th scope="col" style="border-radius: 3px; background-color: #2a43a0;">
<a href="%recipient.urlVote%" target="_blank" style="font-size: 20px; font-family: Helvetica, Arial, sans-serif; color: #ffffff; text-decoration: none; padding: 15px 25px; border-radius: 2px; border: 1px solid #2a43a0; display: inline-block;">
{{#i18n 'common.vote' }}Vote!{{/i18n}}</a></th>
</tr>
</table>
</th>
@ -144,7 +146,7 @@
<p style="margin: 0; text-align: left;">
{{#i18n 'email.copyLink' }}If that doesn't work, copy and paste the following link into your browser:{{/i18n}}
&nbsp;
<a target="_blank" style="color: #2a43a0;">{{invitation_url}}</a>
<a target="_blank" style="color: #2a43a0;">%recipient.urlVote%</a>
</p>
</th>
</tr>
@ -152,9 +154,9 @@
<tr>
<th scope="col" style="background-color: #ffffff; padding: 20px 30px 20px 30px; color: #666666; font-family: 'Lato', Helvetica, Arial, sans-serif; font-size: 18px; font-weight: 400; line-height: 25px;" >
<p style="margin: 0; text-align: left;">
{{#i18n email.linkResult }}The results will be available with the following link when the vote is finished:{{/i18n}}
{{#i18n 'email.linkResult' }}The results will be available with the following link when the vote is finished:{{/i18n}}
&nbsp;
<a target="_blank" style="color: #2a43a0;">{{result_url}}</a>
<a target="_blank" style="color: #2a43a0;">%recipient.urlResult%</a>
</p>
</th>
</tr>

123
functions/send-invite-email/send-invite-email.js

@ -34,57 +34,72 @@ const err = {
};
// setup i18n
i18next.use(Backend).init({
lng: "en",
ns: ["emailInvite", "common"],
defaultNS: "emailInvite",
fallbackNS: "common",
debug: false,
fallbackLng: ["en", "fr"],
backend: {
backends: [FSBackend, HttpApi],
backendOptions: [{ loadPath: "/public/locales/{{lng}}/{{ns}}.json" }, {}],
},
});
// i18next.use(Backend).init({
// lng: "fr",
// ns: ["emailInvite", "common"],
// defaultNS: "emailInvite",
// fallbackNS: "common",
// debug: false,
// fallbackLng: ["fr"],
// backend: {
// backends: [FSBackend, HttpApi],
// backendOptions: [{ loadPath: "/public/locales/{{lng}}/{{ns}}.json" }, {}],
// },
// });
// setup the template engine
// See https://github.com/UUDigitalHumanitieslab/handlebars-i18next
function extend(target, ...sources) {
sources.forEach((source) => {
if (source)
for (let key in source) {
target[key] = source[key];
}
});
return target;
}
Handlebars.registerHelper("i18n", function (key, { hash, data, fn }) {
let parsed = {};
const jsonKeys = [
"lngs",
"fallbackLng",
"ns",
"postProcess",
"interpolation",
];
jsonKeys.forEach((key) => {
if (hash[key]) {
parsed[key] = JSON.parse(hash[key]);
delete hash[key];
}
});
let options = extend({}, data.root.i18next, hash, parsed, {
returnObjects: false,
});
let replace = (options.replace = extend({}, this, options.replace, hash));
delete replace.i18next; // may creep in if this === data.root
if (fn) options.defaultValue = fn(replace);
return new Handlebars.SafeString(i18next.t(key, options));
});
const txtStr = fs.readFileSync(__dirname + "/invite.txt").toString();
const txtTemplate = Handlebars.compile(txtStr);
const htmlStr = fs.readFileSync(__dirname + "/invite.html").toString();
const htmlTemplate = Handlebars.compile(htmlStr);
// function extend(target, ...sources) {
// sources.forEach((source) => {
// if (source)
// for (let key in source) {
// target[key] = source[key];
// }
// });
// return target;
// }
// Handlebars.registerHelper("i18n", function (key, { hash, data, fn }) {
// let parsed = {};
// const jsonKeys = [
// "lngs",
// "fallbackLng",
// "ns",
// "postProcess",
// "interpolation",
// ];
// jsonKeys.forEach((key) => {
// if (hash[key]) {
// parsed[key] = JSON.parse(hash[key]);
// delete hash[key];
// }
// });
// let options = extend({}, data.root.i18next, hash, parsed, {
// returnObjects: false,
// });
// let replace = (options.replace = extend({}, this, options.replace, hash));
// delete replace.i18next; // may creep in if this === data.root
// if (fn) options.defaultValue = fn(replace);
// return new Handlebars.SafeString(i18next.t(key, options));
// });
// const txtStr = fs.readFileSync(__dirname + "/invite.txt").toString();
const txtStr = {
en: fs.readFileSync(__dirname + "/invite-en.txt").toString(),
fr: fs.readFileSync(__dirname + "/invite-fr.txt").toString(),
};
const txtTemplate = {
en: Handlebars.compile(txtStr.en),
fr: Handlebars.compile(txtStr.fr),
};
const htmlStr = {
en: fs.readFileSync(__dirname + "/invite-en.html").toString(),
fr: fs.readFileSync(__dirname + "/invite-fr.html").toString(),
};
const htmlTemplate = {
en: Handlebars.compile(htmlStr.en),
fr: Handlebars.compile(htmlStr.fr),
};
const test = Handlebars.compile("test");
const sendMail = async (event) => {
if (event.httpMethod !== "POST") {
@ -96,23 +111,24 @@ const sendMail = async (event) => {
}
const data = JSON.parse(event.body);
if (!data.recipientVariables || !data.title) {
if (!data.recipientVariables || !data.title || !data.locale) {
return {
statusCode: 422,
body: "Recipient variables and title are required.",
};
}
i18next.changeLanguage(data.locale || "en");
// i18next.changeLanguage(data.locale);
const templateData = {
title: data.title,
};
const mailgunData = {
from: `${i18next.t("Mieux Voter")} <mailgun@mg.app.mieuxvoter.fr>`,
// from: `${i18next.t("Mieux Voter")} <mailgun@mg.app.mieuxvoter.fr>`,
from: '"Mieux Voter" <postmaster@mg.app.mieuxvoter.fr>',
to: Object.keys(data.recipientVariables),
text: txtTemplate(templateData),
html: htmlTemplate(templateData),
text: txtTemplate.fr(templateData),
html: htmlTemplate.fr(templateData),
subject: data.title,
"h:Reply-To": "app@mieuxvoter.fr",
"recipient-variables": JSON.stringify(data.recipientVariables),
@ -121,7 +137,6 @@ const sendMail = async (event) => {
const res = mg.messages
.create("mg.app.mieuxvoter.fr", mailgunData)
.then((msg) => {
console.log(msg);
return success;
}) // logs response data
.catch((err) => {

106
package-lock.json

@ -2054,6 +2054,11 @@
"supports-color": "^5.3.0"
}
},
"charenc": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
"integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc="
},
"chokidar": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz",
@ -2164,6 +2169,22 @@
}
}
},
"cookies": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/cookies/-/cookies-0.8.0.tgz",
"integrity": "sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow==",
"requires": {
"depd": "~2.0.0",
"keygrip": "~1.1.0"
},
"dependencies": {
"depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
}
}
},
"core-js": {
"version": "3.12.1",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.12.1.tgz",
@ -2214,6 +2235,11 @@
"yaml": "^1.10.0"
}
},
"crc-32": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/crc-32/-/crc-32-0.2.2.tgz",
"integrity": "sha1-9EBWigxqRfDuu7V8M7FWAV9PBLY="
},
"create-ecdh": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz",
@ -2263,6 +2289,11 @@
"node-fetch": "2.6.1"
}
},
"crypt": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
"integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs="
},
"crypto-browserify": {
"version": "3.12.0",
"resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
@ -2783,6 +2814,11 @@
}
}
},
"handlebars-i18next": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/handlebars-i18next/-/handlebars-i18next-1.0.1.tgz",
"integrity": "sha512-m5sxMthNYFXDYkj7r1MhSiW4tqfIfEKYlUn4TtdXSJT+r6YA9zQddd01BGOgDj3TmjJQc6bDiUQgQVEkluaSdg=="
},
"has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
@ -2899,6 +2935,11 @@
}
}
},
"i18next-client": {
"version": "1.10.3",
"resolved": "https://registry.npmjs.org/i18next-client/-/i18next-client-1.10.3.tgz",
"integrity": "sha1-dtA1NVftkNHnqHdU1QBNP3gB/ek="
},
"i18next-fs-backend": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/i18next-fs-backend/-/i18next-fs-backend-1.1.1.tgz",
@ -2920,6 +2961,34 @@
"@babel/runtime": "^7.4.5"
}
},
"i18next-text": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/i18next-text/-/i18next-text-0.5.6.tgz",
"integrity": "sha1-nxPB5DKmoOYaRNtYINeS/IizRmI=",
"requires": {
"crc-32": "^0.2.2",
"i18next": "^1.7.10",
"md5": "^2.0.0",
"sha1": "^1.1.0"
},
"dependencies": {
"i18next": {
"version": "1.10.6",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-1.10.6.tgz",
"integrity": "sha1-/d2LSRUCxIlnpiljvHIv+JfN3qA=",
"requires": {
"cookies": ">= 0.2.2",
"i18next-client": "1.10.3",
"json5": "^0.2.0"
}
},
"json5": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/json5/-/json5-0.2.0.tgz",
"integrity": "sha1-ttcDXHDEVw+IPH7cdZ3jrgPbM0M="
}
}
},
"iconv-lite": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz",
@ -2989,6 +3058,11 @@
"call-bind": "^1.0.0"
}
},
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
},
"is-callable": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz",
@ -3158,6 +3232,14 @@
"minimist": "^1.2.0"
}
},
"keygrip": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz",
"integrity": "sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==",
"requires": {
"tsscmp": "1.0.6"
}
},
"kind-of": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
@ -3257,6 +3339,16 @@
"semver": "^6.0.0"
}
},
"md5": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz",
"integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==",
"requires": {
"charenc": "0.0.2",
"crypt": "0.0.2",
"is-buffer": "~1.1.6"
}
},
"md5.js": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
@ -4208,6 +4300,15 @@
"safe-buffer": "^5.0.1"
}
},
"sha1": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/sha1/-/sha1-1.1.1.tgz",
"integrity": "sha1-rdqnqTFo85PxnrKxUJFhjicA+Eg=",
"requires": {
"charenc": ">= 0.0.1",
"crypt": ">= 0.0.1"
}
},
"shallow-clone": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",
@ -4445,6 +4546,11 @@
"resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.2.0.tgz",
"integrity": "sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw=="
},
"tsscmp": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz",
"integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA=="
},
"tty-browserify": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz",

2
package.json

@ -22,11 +22,13 @@
"dotenv": "^8.6.0",
"form-data": "^4.0.0",
"handlebars": "^4.7.7",
"handlebars-i18next": "^1.0.1",
"i18next": "^20.2.2",
"i18next-chained-backend": "^2.1.0",
"i18next-fs-backend": "^1.1.1",
"i18next-http-backend": "^1.2.4",
"i18next-localstorage-backend": "^3.1.2",
"i18next-text": "^0.5.6",
"mailgun.js": "^3.3.2",
"next": "^10.2.0",
"next-i18next": "^8.2.0",

2
pages/new/confirm/[pid].jsx

@ -147,7 +147,7 @@ const ConfirmElection = ({
<Row className="mt-4 mb-4 justify-content-md-center">
{participate}
<Col className="text-center col-lg-3">
<Link href={`/vote/${pid}`}>
<Link href={`/result/${pid}`}>
<a target="_blank" rel="noreferrer" className="btn btn-secondary">
<FontAwesomeIcon icon={faPollH} className="mr-2" />
{t("resource.resultsBtn")}

2
pages/new/index.js

@ -165,7 +165,7 @@ const CreateElection = (props) => {
title,
candidates.map((c) => c.label).filter((c) => c !== ""),
{
emails,
mails: emails,
numGrades,
start: start.getTime() / 1000,
finish: finish.getTime() / 1000,

26
services/api.js

@ -18,6 +18,10 @@ const sendInviteMail = (res) => {
*/
const { title, mails, tokens, locale } = res;
if (!mails || !mails.length) {
throw new Error("No emails are provided.");
}
if (mails.length !== tokens.length) {
throw new Error("The number of emails differ from the number of tokens");
}
@ -49,12 +53,7 @@ const sendInviteMail = (res) => {
}),
});
return Promise.all([
new Promise((resolve, reject) => {
resolve(res);
}),
req,
]);
return req.then((any) => res);
};
const createElection = (
@ -64,7 +63,7 @@ const createElection = (
/**
* Create an election from its title, its candidates and a bunch of options
*/
emails,
mails,
numGrades,
start,
finish,
@ -77,6 +76,7 @@ const createElection = (
const endpoint = new URL(api.routesServer.setElection, api.urlServer);
console.log(endpoint.href);
const onInvitationOnly = mails && mails.length > 0;
fetch(endpoint.href, {
method: "POST",
@ -86,9 +86,9 @@ const createElection = (
body: JSON.stringify({
title,
candidates,
on_invitation_only: emails.length > 0,
on_invitation_only: onInvitationOnly,
num_grades: numGrades,
elector_emails: emails || [],
elector_emails: mails || [],
start_at: start,
finish_at: finish,
select_language: locale || "en",
@ -103,8 +103,12 @@ const createElection = (
}
return response.json();
})
.then((res) => sendInviteMail({ locale, mails: emails || [], ...res }))
.then((res) => res[0]) // remove response from mail invitations
.then((res) => {
if (onInvitationOnly) {
return sendInviteMail({ locale, mails: mails, ...res });
}
return res;
})
.then(successCallback)
.catch(failureCallback || console.log);
};

Loading…
Cancel
Save