Send mailing list emails

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
Sacha Al Himdani
2026-03-05 18:05:41 +01:00
parent aa01c40184
commit 85ec106cd6
67 changed files with 7615 additions and 160 deletions

View File

@@ -219,6 +219,9 @@ const (
subjectTrustCenterDocumentAccessRejected = "Compliance Page Document Access Rejected - %s"
subjectMagicLink = "Connect to %s"
subjectElectronicSignatureCertificate = "Your signed %s - Certificate of Completion"
subjectMailingListSubscription = "%s – Confirm Your Compliance Updates Subscription"
subjectMailingListUnsubscription = "%s – You've been unsubscribed"
subjectMailingListUpdates = "%s – %s"
)
var (
@@ -242,6 +245,12 @@ var (
magicLinkTextTemplate = texttemplate.Must(texttemplate.ParseFS(Templates, "dist/magic-link.txt.tmpl"))
electronicSignatureCertificateHTMLTemplate = htmltemplate.Must(htmltemplate.ParseFS(Templates, "dist/electronic-signature-certificate.html.tmpl"))
electronicSignatureCertificateTextTemplate = texttemplate.Must(texttemplate.ParseFS(Templates, "dist/electronic-signature-certificate.txt.tmpl"))
mailingListSubscriptionHTMLTemplate = htmltemplate.Must(htmltemplate.ParseFS(Templates, "dist/mailing-list-subscription.html.tmpl"))
mailingListSubscriptionTextTemplate = texttemplate.Must(texttemplate.ParseFS(Templates, "dist/mailing-list-subscription.txt.tmpl"))
mailingListUnsubscriptionHTMLTemplate = htmltemplate.Must(htmltemplate.ParseFS(Templates, "dist/mailing-list-unsubscription.html.tmpl"))
mailingListUnsubscriptionTextTemplate = texttemplate.Must(texttemplate.ParseFS(Templates, "dist/mailing-list-unsubscription.txt.tmpl"))
mailingListUpdatesHTMLTemplate = htmltemplate.Must(htmltemplate.ParseFS(Templates, "dist/mailing-list-updates.html.tmpl"))
mailingListUpdatesTextTemplate = texttemplate.Must(texttemplate.ParseFS(Templates, "dist/mailing-list-updates.txt.tmpl"))
)
func (p *Presenter) getCommonVariables(ctx context.Context) (*CommonVariables, error) {
@@ -489,6 +498,84 @@ func (p *Presenter) RenderElectronicSignatureCertificate(ctx context.Context, si
return fmt.Sprintf(subjectElectronicSignatureCertificate, documentType), textBody, htmlBody, err
}
func (p *Presenter) RenderMailingListSubscription(ctx context.Context, organizationName string, confirmURL string, unsubscribeURL string) (subject string, textBody string, htmlBody *string, err error) {
vars, err := p.getCommonVariables(ctx)
if err != nil {
return "", "", nil, fmt.Errorf("cannot get common variables: %w", err)
}
data := struct {
*CommonVariables
OrganizationName string
ConfirmURL string
UnsubscribeURL string
}{
CommonVariables: vars,
OrganizationName: organizationName,
ConfirmURL: confirmURL,
UnsubscribeURL: unsubscribeURL,
}
textBody, htmlBody, err = renderEmail(mailingListSubscriptionTextTemplate, mailingListSubscriptionHTMLTemplate, data)
if err != nil {
return "", "", nil, err
}
return fmt.Sprintf(subjectMailingListSubscription, organizationName), textBody, htmlBody, nil
}
func (p *Presenter) RenderMailingListUnsubscription(ctx context.Context, organizationName string) (subject string, textBody string, htmlBody *string, err error) {
vars, err := p.getCommonVariables(ctx)
if err != nil {
return "", "", nil, fmt.Errorf("cannot get common variables: %w", err)
}
data := struct {
*CommonVariables
OrganizationName string
}{
CommonVariables: vars,
OrganizationName: organizationName,
}
textBody, htmlBody, err = renderEmail(mailingListUnsubscriptionTextTemplate, mailingListUnsubscriptionHTMLTemplate, data)
if err != nil {
return "", "", nil, err
}
return fmt.Sprintf(subjectMailingListUnsubscription, organizationName), textBody, htmlBody, nil
}
func (p *Presenter) RenderMailingListNews(ctx context.Context, organizationName string, newsTitle string, newsBody string, compliancePageURL string, unsubscribeURL string) (subject string, textBody string, htmlBody *string, err error) {
vars, err := p.getCommonVariables(ctx)
if err != nil {
return "", "", nil, fmt.Errorf("cannot get common variables: %w", err)
}
data := struct {
*CommonVariables
OrganizationName string
NewsTitle string
NewsBody string
CompliancePageURL string
UnsubscribeURL string
}{
CommonVariables: vars,
OrganizationName: organizationName,
NewsTitle: newsTitle,
NewsBody: newsBody,
CompliancePageURL: compliancePageURL,
UnsubscribeURL: unsubscribeURL,
}
textBody, htmlBody, err = renderEmail(mailingListUpdatesTextTemplate, mailingListUpdatesHTMLTemplate, data)
if err != nil {
return "", "", nil, err
}
return fmt.Sprintf(subjectMailingListUpdates, organizationName, newsTitle), textBody, htmlBody, nil
}
func renderEmail(textTemplate *texttemplate.Template, htmlTemplate *htmltemplate.Template, data any) (textBody string, htmlBody *string, err error) {
var textBuf bytes.Buffer
if err := textTemplate.Execute(&textBuf, data); err != nil {

View File

@@ -4,6 +4,7 @@ import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import * as React from "react";
import MailingListUpdates from "../src/MailingListUpdates";
import ConfirmEmail from "../src/ConfirmEmail";
import DocumentExport from "../src/DocumentExport";
import DocumentSigning from "../src/DocumentSigning";
@@ -13,6 +14,8 @@ import PasswordReset from "../src/PasswordReset";
import TrustCenterAccess from "../src/TrustCenterAccess";
import TrustCenterDocumentAccessRejected from "../src/TrustCenterDocumentAccessRejected";
import ElectronicSignatureCertificate from "../src/ElectronicSignatureCertificate";
import MailingListSubscription from "../src/MailingListSubscription";
import MailingListUnsubscription from "../src/MailingListUnsubscription";
import MagicLink from "../src/MagicLink";
const __filename = fileURLToPath(import.meta.url);
@@ -64,6 +67,18 @@ const templates: TemplateConfig[] = [
name: "electronic-signature-certificate",
render: () => ElectronicSignatureCertificate(),
},
{
name: "mailing-list-subscription",
render: () => MailingListSubscription(),
},
{
name: "mailing-list-unsubscription",
render: () => MailingListUnsubscription(),
},
{
name: "mailing-list-updates",
render: () => MailingListUpdates(),
},
];
async function build() {

View File

@@ -0,0 +1,46 @@
import { Button, Hr, Link, Section, Text } from "@react-email/components";
import * as React from "react";
import EmailLayout, {
bodyText,
button,
buttonContainer,
footerText,
} from "./components/EmailLayout";
export const MailingListSubscription = () => {
return (
<EmailLayout
subject={`${"{{.OrganizationName}}"} – Confirm Your Compliance Updates Subscription`}
>
<Text style={bodyText}>
You requested to subscribe to compliance updates from{" "}
<strong>{"{{.OrganizationName}}"}</strong>.
</Text>
<Text style={bodyText}>
Please click the button below to confirm your subscription.
</Text>
<Section style={buttonContainer}>
<Button style={button} href={"{{.ConfirmURL}}"}>
Confirm Subscription
</Button>
</Section>
<Hr style={{ borderColor: "#ecefec", margin: "8px 0 20px" }} />
<Text style={{ ...footerText, textAlign: "center" }}>
If you did not request this subscription, or no longer want to receive
these emails,{" "}
<Link
href={"{{.UnsubscribeURL}}"}
style={{ color: "#374151", textDecoration: "underline", fontWeight: "500" }}
>
unsubscribe here.
</Link>
</Text>
</EmailLayout>
);
};
export default MailingListSubscription;

View File

@@ -0,0 +1,26 @@
import { Hr, Text } from "@react-email/components";
import * as React from "react";
import EmailLayout, { bodyText, footerText } from "./components/EmailLayout";
export const MailingListUnsubscription = () => {
return (
<EmailLayout
subject={`${"{{.OrganizationName}}"} – You've been unsubscribed`}
>
<Text style={bodyText}>
You've been successfully unsubscribed from{" "}
<strong>{"{{.OrganizationName}}"}</strong>'s compliance updates. You
will no longer receive notifications when new information is published
on their compliance page.
</Text>
<Hr style={{ borderColor: "#ecefec", margin: "8px 0 20px" }} />
<Text style={{ ...footerText, textAlign: "center" }}>
This email was sent to confirm your unsubscription request.
</Text>
</EmailLayout>
);
};
export default MailingListUnsubscription;

View File

@@ -0,0 +1,48 @@
import { Button, Hr, Link, Section, Text } from "@react-email/components";
import * as React from "react";
import EmailLayout, {
bodyText,
button,
buttonContainer,
footerText,
} from "./components/EmailLayout";
export const MailingListUpdates = () => {
return (
<EmailLayout
subject={`${"{{.OrganizationName}}"} – ${"{{.NewsTitle}}"}`}
>
<Text style={bodyText}>
{"{{.OrganizationName}}"} has published a new compliance update.
</Text>
<Text style={{ ...bodyText, fontWeight: "600", fontSize: "18px" }}>
{"{{.NewsTitle}}"}
</Text>
<Text style={bodyText}>{"{{.NewsBody}}"}</Text>
<Section style={buttonContainer}>
<Button style={button} href={"{{.CompliancePageURL}}"}>
View Compliance Page
</Button>
</Section>
<Hr style={{ borderColor: "#ecefec", margin: "8px 0 20px" }} />
<Text style={{ ...footerText, textAlign: "center" }}>
You are receiving this email because you subscribed to compliance
updates from <strong>{"{{.OrganizationName}}"}</strong>. To
unsubscribe,{" "}
<Link
href={"{{.UnsubscribeURL}}"}
style={{ color: "#374151", textDecoration: "underline", fontWeight: "500" }}
>
click here.
</Link>
</Text>
</EmailLayout>
);
};
export default MailingListUpdates;

View File

@@ -0,0 +1,16 @@
{{.SenderCompanyName}}
Hi {{.RecipientFullName}},
You requested to subscribe to compliance updates from {{.OrganizationName}}.
Please click the link below to confirm your subscription:
{{.ConfirmURL}}
If you did not request this subscription, or no longer want to receive these emails, unsubscribe here:
{{.UnsubscribeURL}}
{{.SenderCompanyHeadquarterAddress}}
Powered By Probo

View File

@@ -0,0 +1,8 @@
{{.SenderCompanyName}}
Hi {{.RecipientFullName}},
You've been successfully unsubscribed from {{.OrganizationName}}'s compliance updates. You will no longer receive notifications when new information is published on their compliance page.
{{.SenderCompanyHeadquarterAddress}}
Powered By Probo

View File

@@ -0,0 +1,22 @@
{{.SenderCompanyName}}
Hi {{.RecipientFullName}},
{{.OrganizationName}} has published a new compliance update.
{{.NewsTitle}}
{{.NewsBody}}
View the compliance page:
{{.CompliancePageURL}}
You are receiving this email because you subscribed to compliance updates from {{.OrganizationName}}.
To unsubscribe, visit:
{{.UnsubscribeURL}}
{{.SenderCompanyHeadquarterAddress}}
Powered By Probo