feat: Introduce a new mail package for email templates and update the gatekeeper login page with new logo assets.
Some checks failed
Monorepo Pipeline / 🧪 Quality Assurance (push) Failing after 42s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build & Push Images (push) Has been skipped

This commit is contained in:
2026-02-05 01:05:49 +01:00
parent 646d615e76
commit 95d0a1622f
15 changed files with 1149 additions and 4 deletions

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 17 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -0,0 +1,29 @@
import * as React from "react";
import { Link, Img } from "@react-email/components";
export interface MintelLogoProps {
size?: number;
}
export const MintelLogo = ({ size = 200 }: MintelLogoProps) => {
// Original Logo is 545x260, we scale it
const width = size;
const height = (size * 260) / 545;
return (
<Link
href="https://mintel.me"
style={{
textDecoration: "none",
display: "inline-block",
}}
>
<Img
src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+Cjxzdmcgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDU0NSAyNjAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgeG1sbnM6c2VyaWY9Imh0dHA6Ly93d3cuc2VyaWYuY29tLyIgc3R5bGU9ImZpbGwtcnVsZTpldmVub2RkO2NsaXAtcnVsZTpldmVub2RkO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoyOyI+CiAgICA8ZyB0cmFuc2Zvcm09Im1hdHJpeCgxLDAsMCwxLC0xMjg2LC0xMTUwKSI+CiAgICAgICAgPGcgdHJhbnNmb3JtPSJtYXRyaXgoMSwtMCwtMCwxLDEyODYsMTE1MCkiPgogICAgICAgICAgICA8dXNlIHhsaW5rOmhyZWY9IiNfSW1hZ2UxIiB4PSI0MS41NjkiIHk9IjMxLjM4NSIgd2lkdGg9IjQ2MnB4IiBoZWlnaHQ9IjE5N3B4Ii8+CiAgICAgICAgPC9nPgogICAgPC9nPgogICAgPGRlZnM+CiAgICAgICAgPGltYWdlIGlkPSJfSW1hZ2UxIiB3aWR0aD0iNDYycHgiIGhlaWdodD0iMTk3cHgiIHhsaW5rOmhyZWY9ImRhdGE6aW1hZ2UvcG5nO2Jhc2U2NCxpVkJPUncwS0dnb0FBQU5TVWhFVWdBQUFjNFNBQUFERkNBWUFBQUNYQlJXMEFBQWdBRWxFUVZSNFhMU0JTQlNScGRmZWU0eE9lTXVlemV6NnoxY3VaeXpNeXpNeDU3cnI5ZXY5ZTFlNTVxNU56WXpNeFpPYmRkNWRNdG93MHcwMHcwMHcwMHcwMHV0eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4cDVxNHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4O0VYSVRDT0RFOiAwIgogICAgPC9kZWZzPgo8L3N2Zz4K"
alt="Mintel Logo"
width={width}
height={height}
/>
</Link>
);
};

View File

@@ -0,0 +1,24 @@
import { render as reactEmailRender } from "@react-email/components";
import { ReactElement } from "react";
/**
* Renders a React email template to HTML.
*/
export async function render(
template: ReactElement,
options?: any,
): Promise<string> {
return reactEmailRender(template, options);
}
// Export Components
export * from "./components/MintelLogo";
// Export Layouts
export * from "./layouts/BaseLayout";
export * from "./layouts/MintelLayout";
export * from "./layouts/ClientLayout";
// Export Templates
export * from "./templates/ContactFormNotification";
export * from "./templates/ConfirmationMessage";

View File

@@ -0,0 +1,53 @@
import {
Body,
Container,
Head,
Html,
Preview,
Section,
} from "@react-email/components";
import * as React from "react";
export interface BaseLayoutProps {
preview: string;
children: React.ReactNode;
brandColor?: string;
}
export const BaseLayout = ({
preview,
children,
brandColor = "#82ed20",
}: BaseLayoutProps) => {
return (
<Html>
<Head />
<Preview>{preview}</Preview>
<Body style={main}>
<Container style={container}>
<Section style={content}>{children}</Section>
</Container>
</Body>
</Html>
);
};
const main = {
backgroundColor: "#0a0a0a",
color: "#ffffff",
fontFamily:
'-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Ubuntu,sans-serif',
};
const container = {
backgroundColor: "#0f0f0f",
margin: "0 auto",
padding: "40px 0",
maxWidth: "600px",
border: "1px solid #1a1a1a",
borderRadius: "12px",
};
const content = {
padding: "0 40px",
};

View File

@@ -0,0 +1,80 @@
import * as React from "react";
import { Hr, Section, Text, Img } from "@react-email/components";
import { BaseLayout } from "./BaseLayout";
export interface ClientLayoutProps {
preview: string;
children: React.ReactNode;
clientLogo?: string;
clientName: string;
brandColor?: string;
}
export const ClientLayout = ({
preview,
children,
clientLogo,
clientName,
brandColor = "#82ed20",
}: ClientLayoutProps) => {
return (
<BaseLayout preview={preview} brandColor={brandColor}>
<Section style={header}>
{clientLogo ? (
<Img src={clientLogo} alt={clientName} height="40" style={logo} />
) : (
<Text style={logoText(brandColor)}>{clientName}</Text>
)}
</Section>
<Hr style={hr} />
<Section style={mainContent}>{children}</Section>
<Hr style={hr} />
<Section style={footer}>
<Text style={footerText}>
&copy; 2026 {clientName}. All rights reserved.
</Text>
</Section>
</BaseLayout>
);
};
const header = {
marginBottom: "32px",
};
const logo = {
margin: "0 auto",
display: "block",
};
const logoText = (color: string) => ({
margin: "0 auto",
textAlign: "center" as const,
fontSize: "24px",
fontWeight: 900,
color: "#ffffff",
letterSpacing: "-0.02em",
borderLeft: `4px solid ${color}`,
paddingLeft: "12px",
});
const mainContent = {
marginBottom: "32px",
};
const hr = {
borderColor: "#222222",
margin: "20px 0",
};
const footer = {
marginTop: "32px",
textAlign: "center" as const,
};
const footerText = {
fontSize: "10px",
color: "#333333",
textTransform: "uppercase" as const,
letterSpacing: "0.1em",
};

View File

@@ -0,0 +1,53 @@
import * as React from "react";
import { Hr, Section, Text } from "@react-email/components";
import { BaseLayout } from "./BaseLayout";
import { MintelLogo } from "../components/MintelLogo";
export interface MintelLayoutProps {
preview: string;
children: React.ReactNode;
}
export const MintelLayout = ({ preview, children }: MintelLayoutProps) => {
return (
<BaseLayout preview={preview} brandColor="#82ed20">
<Section style={header}>
<MintelLogo />
</Section>
<Hr style={hr} />
<Section style={mainContent}>{children}</Section>
<Hr style={hr} />
<Section style={footer}>
<Text style={footerText}>
&copy; 2026 Mintel Infrastructure. Secure Communication Channel.
</Text>
</Section>
</BaseLayout>
);
};
const header = {
marginBottom: "32px",
};
const mainContent = {
marginBottom: "32px",
};
const hr = {
borderColor: "#222222",
margin: "20px 0",
};
const footer = {
marginTop: "32px",
textAlign: "center" as const,
};
const footerText = {
fontSize: "12px",
color: "#444444",
fontWeight: 700,
textTransform: "uppercase" as const,
letterSpacing: "0.1em",
};

View File

@@ -0,0 +1,57 @@
import * as React from "react";
import { Heading, Text } from "@react-email/components";
import { ClientLayout } from "../layouts/ClientLayout";
export interface ConfirmationMessageProps {
name: string;
clientName: string;
clientLogo?: string;
brandColor?: string;
}
export const ConfirmationMessage = ({
name,
clientName,
clientLogo,
brandColor,
}: ConfirmationMessageProps) => {
const preview = `Thank you for your message, ${name}`;
return (
<ClientLayout
preview={preview}
clientName={clientName}
clientLogo={clientLogo}
brandColor={brandColor}
>
<Heading style={h1}>Thank You</Heading>
<Text style={text}>Hello {name},</Text>
<Text style={text}>
Thank you for contacting us. We have received your message and will get
back to you as soon as possible.
</Text>
<Text style={text}>
Best regards,
<br />
The {clientName} Team
</Text>
</ClientLayout>
);
};
export default ConfirmationMessage;
const h1 = {
fontSize: "28px",
fontWeight: "900",
margin: "0 0 16px",
color: "#ffffff",
letterSpacing: "-0.04em",
};
const text = {
fontSize: "16px",
lineHeight: "24px",
color: "#cccccc",
margin: "16px 0",
};

View File

@@ -0,0 +1,118 @@
import * as React from "react";
import { Heading, Section, Text, Row, Column } from "@react-email/components";
import { MintelLayout } from "../layouts/MintelLayout";
export interface ContactFormNotificationProps {
name: string;
email: string;
message: string;
productName?: string;
}
export const ContactFormNotification = ({
name,
email,
message,
productName,
}: ContactFormNotificationProps) => {
const preview = `New message from ${name}`;
return (
<MintelLayout preview={preview}>
<Heading style={h1}>New Submission</Heading>
<Text style={intro}>
A new message has been received via the contact form.
</Text>
<Section style={detailsContainer}>
<Row>
<Column style={labelCol}>
<Text style={label}>Name</Text>
</Column>
<Column>
<Text style={value}>{name}</Text>
</Column>
</Row>
<Row>
<Column style={labelCol}>
<Text style={label}>Email</Text>
</Column>
<Column>
<Text style={value}>{email}</Text>
</Column>
</Row>
{productName && (
<Row>
<Column style={labelCol}>
<Text style={label}>Product</Text>
</Column>
<Column>
<Text style={value}>{productName}</Text>
</Column>
</Row>
)}
</Section>
<Section style={messageSection}>
<Text style={label}>Message</Text>
<Text style={messageText}>{message}</Text>
</Section>
</MintelLayout>
);
};
export default ContactFormNotification;
const h1 = {
fontSize: "28px",
fontWeight: "900",
margin: "0 0 16px",
color: "#ffffff",
letterSpacing: "-0.04em",
};
const intro = {
fontSize: "16px",
color: "#888888",
margin: "0 0 32px",
};
const detailsContainer = {
backgroundColor: "#151515",
padding: "24px",
borderRadius: "8px",
marginBottom: "24px",
};
const labelCol = {
width: "100px",
};
const label = {
fontSize: "10px",
fontWeight: "900",
textTransform: "uppercase" as const,
color: "#444444",
margin: "0 0 4px",
letterSpacing: "0.1em",
};
const value = {
fontSize: "16px",
color: "#ffffff",
margin: "0 0 12px",
};
const messageSection = {
padding: "0 24px",
};
const messageText = {
fontSize: "16px",
lineHeight: "24px",
color: "#cccccc",
fontStyle: "italic",
borderLeft: "2px solid #222222",
paddingLeft: "16px",
margin: "12px 0 0",
};