feat: create email template for TTO reservation confirmation
feat: implement order notifications service with TTO reservation confirmation handling feat: create migration for TTO booking email webhook trigger
This commit is contained in:
@@ -4,17 +4,20 @@
|
||||
"version": "0.1.0",
|
||||
"scripts": {
|
||||
"clean": "git clean -xdf .turbo node_modules",
|
||||
"typecheck": "tsc --noEmit"
|
||||
"typecheck": "tsc --noEmit",
|
||||
"email:dev": "email dev --dir src/emails --port 3001"
|
||||
},
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-email/components": "0.0.41"
|
||||
"@react-email/components": "0.0.41",
|
||||
"react-email": "4.2.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@kit/i18n": "workspace:*",
|
||||
"@kit/tsconfig": "workspace:*"
|
||||
"@kit/tsconfig": "workspace:*",
|
||||
"@react-email/preview-server": "4.2.12"
|
||||
},
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
|
||||
@@ -6,22 +6,28 @@ import { EmailFooter } from './footer';
|
||||
export default function CommonFooter({ t }: { t: TFunction }) {
|
||||
const namespace = 'common';
|
||||
|
||||
const lines = [
|
||||
t(`${namespace}:footer.lines1`),
|
||||
t(`${namespace}:footer.lines2`),
|
||||
t(`${namespace}:footer.lines3`),
|
||||
t(`${namespace}:footer.lines4`),
|
||||
];
|
||||
|
||||
return (
|
||||
<EmailFooter>
|
||||
{lines.map((line, index) => (
|
||||
<Text
|
||||
key={index}
|
||||
className="text-[16px] leading-[24px] text-[#242424]"
|
||||
dangerouslySetInnerHTML={{ __html: line }}
|
||||
/>
|
||||
))}
|
||||
<Text className="text-[16px] leading-[24px] text-[#242424]">
|
||||
{t(`${namespace}:footer.title`)}
|
||||
</Text>
|
||||
<Text className="text-[16px] leading-[24px] text-[#242424]">
|
||||
{t(`${namespace}:footer.emailField`)}{' '}
|
||||
<a href={`mailto:${t(`${namespace}:footer.title`)}`}>
|
||||
{t(`${namespace}:footer.email`)}
|
||||
</a>
|
||||
</Text>
|
||||
<Text className="text-[16px] leading-[24px] text-[#242424]">
|
||||
{t(`${namespace}:footer.phoneField`)}{' '}
|
||||
<a href={`tel:${t(`${namespace}:footer.phone`)}`}>
|
||||
{t(`${namespace}:footer.phone`)}
|
||||
</a>
|
||||
</Text>
|
||||
<Text className="text-[16px] leading-[24px] text-[#242424]">
|
||||
<a href={`https://${t(`${namespace}:footer.website`)}`}>
|
||||
{t(`${namespace}:footer.website`)}
|
||||
</a>
|
||||
</Text>
|
||||
</EmailFooter>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Container, Text } from '@react-email/components';
|
||||
import { Container, Section } from '@react-email/components';
|
||||
|
||||
export function EmailFooter(props: React.PropsWithChildren) {
|
||||
return (
|
||||
<Container className="mt-[24px]">
|
||||
<Text className="px-4 text-[12px] leading-[20px] text-gray-300">
|
||||
<Section className="px-4 text-[12px] leading-[20px] text-gray-300">
|
||||
{props.children}
|
||||
</Text>
|
||||
</Section>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
8
packages/email-templates/src/emails/email.tsx
Normal file
8
packages/email-templates/src/emails/email.tsx
Normal file
@@ -0,0 +1,8 @@
|
||||
import React from 'react';
|
||||
|
||||
const Email = () => {
|
||||
return <div>Email</div>;
|
||||
};
|
||||
|
||||
export default Email;
|
||||
Email.PreviewProps = {};
|
||||
@@ -0,0 +1,137 @@
|
||||
import {
|
||||
Body,
|
||||
Head,
|
||||
Html,
|
||||
Link,
|
||||
Preview,
|
||||
Tailwind,
|
||||
Text,
|
||||
render,
|
||||
} from '@react-email/components';
|
||||
|
||||
import { BodyStyle } from '../components/body-style';
|
||||
import CommonFooter from '../components/common-footer';
|
||||
import { EmailContent } from '../components/content';
|
||||
import { EmailHeader } from '../components/header';
|
||||
import { EmailHeading } from '../components/heading';
|
||||
import { EmailWrapper } from '../components/wrapper';
|
||||
import { initializeEmailI18n } from '../lib/i18n';
|
||||
|
||||
export async function renderTtoReservationConfirmationEmail({
|
||||
language,
|
||||
recipientName,
|
||||
startTime,
|
||||
orderName,
|
||||
locationName,
|
||||
locationAddress,
|
||||
orderId,
|
||||
serviceProviderName,
|
||||
serviceProviderEmail,
|
||||
serviceProviderPhone,
|
||||
}: {
|
||||
language: string;
|
||||
recipientName: string;
|
||||
startTime: string;
|
||||
orderName: string;
|
||||
locationName?: string;
|
||||
locationAddress?: string | null;
|
||||
orderId: string;
|
||||
serviceProviderName?: string;
|
||||
serviceProviderEmail?: string | null;
|
||||
serviceProviderPhone?: string | null;
|
||||
}) {
|
||||
const namespace = 'tto-reservation-confirmation-email';
|
||||
|
||||
const { t } = await initializeEmailI18n({
|
||||
language,
|
||||
namespace: [namespace, 'common'],
|
||||
});
|
||||
|
||||
const previewText = t(`${namespace}:previewText`, {
|
||||
reservation: orderName,
|
||||
});
|
||||
|
||||
const subject = t(`${namespace}:subject`, {
|
||||
reservation: orderName,
|
||||
});
|
||||
|
||||
const email = (
|
||||
<Html>
|
||||
<Head>
|
||||
<BodyStyle />
|
||||
</Head>
|
||||
|
||||
<Preview>{previewText}</Preview>
|
||||
|
||||
<Tailwind>
|
||||
<Body>
|
||||
<EmailWrapper>
|
||||
<EmailContent>
|
||||
<EmailHeader>
|
||||
<EmailHeading>{previewText}</EmailHeading>
|
||||
</EmailHeader>
|
||||
<Text className="text-[16px] leading-[24px] text-[#242424]">
|
||||
{t(`${namespace}:helloName`, { name: recipientName })}
|
||||
</Text>
|
||||
<Text className="text-[16px] leading-[24px] text-[#242424]">
|
||||
{t(`${namespace}:thankYou`)}
|
||||
</Text>
|
||||
<Text className="text-[16px] leading-[24px] text-[#242424]">
|
||||
{orderName}, {new Date(startTime).toLocaleString()},{' '}
|
||||
{locationAddress}, {locationName}
|
||||
</Text>
|
||||
<Text className="text-[16px] leading-[24px] text-[#242424]">
|
||||
<Link
|
||||
href={`${process.env.NEXT_PUBLIC_SITE_URL}/home/order/${orderId}`}
|
||||
>
|
||||
{t(`${namespace}:viewOrder`)}
|
||||
</Link>
|
||||
</Text>
|
||||
<Text className="mt-10 text-[16px] leading-[24px] text-[#242424]">
|
||||
{serviceProviderName}
|
||||
</Text>
|
||||
<Text className="text-[16px] leading-[24px] text-[#242424]">
|
||||
{t(`${namespace}:customerSupport`)}
|
||||
<Link href={`tel:${serviceProviderPhone})}`}>
|
||||
{serviceProviderPhone}
|
||||
</Link>
|
||||
</Text>
|
||||
<Link href={`mailto:${serviceProviderEmail}`}>
|
||||
{serviceProviderEmail}
|
||||
</Link>
|
||||
|
||||
<CommonFooter t={t} />
|
||||
</EmailContent>
|
||||
</EmailWrapper>
|
||||
</Body>
|
||||
</Tailwind>
|
||||
</Html>
|
||||
);
|
||||
|
||||
const html = await render(email);
|
||||
|
||||
return {
|
||||
html,
|
||||
subject,
|
||||
email,
|
||||
};
|
||||
}
|
||||
|
||||
const PreviewEmail = async () => {
|
||||
const { email } = await renderTtoReservationConfirmationEmail({
|
||||
language: 'et',
|
||||
recipientName: 'John Doe',
|
||||
startTime: '2025-09-27 05:45:00+00',
|
||||
orderName: 'Hambaarst',
|
||||
locationName: 'Tallinn',
|
||||
locationAddress: 'Põhja puiestee, 2/3A',
|
||||
orderId: '123123',
|
||||
serviceProviderName: 'Dentas OÜ',
|
||||
serviceProviderEmail: 'email@example.ee',
|
||||
serviceProviderPhone: '+372111111',
|
||||
});
|
||||
return email;
|
||||
};
|
||||
|
||||
export default PreviewEmail;
|
||||
PreviewEmail.PreviewProps = {};
|
||||
@@ -11,3 +11,4 @@ export * from './emails/order-processing.email';
|
||||
export * from './emails/patient-first-results-received.email';
|
||||
export * from './emails/patient-full-results-received.email';
|
||||
export * from './emails/book-time-failed.email';
|
||||
export * from './emails/tto-reservation-confirmation.email';
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"previewText": "Your booking is confirmed! - {{reservation}}",
|
||||
"subject": "Your booking is confirmed! - {{reservation}}",
|
||||
"helloName": "Hello, {{name}}!",
|
||||
"thankYou": "Thank you for your order!",
|
||||
"viewOrder": "See booking",
|
||||
"customerSupport": "Customer support: "
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
{
|
||||
"footer": {
|
||||
"lines1": "MedReport",
|
||||
"lines2": "E-mail: <a href=\"mailto:info@medreport.ee\">info@medreport.ee</a>",
|
||||
"lines3": "Klienditugi: <a href=\"tel:+37258871517\">+372 5887 1517</a>",
|
||||
"lines4": "<a href=\"https://www.medreport.ee\">www.medreport.ee</a>"
|
||||
"title": "MedReport",
|
||||
"emailField": "E-mail:",
|
||||
"email": "info@medreport.ee",
|
||||
"phoneField": "Klienditugi:",
|
||||
"phone": "+372 5887 1517",
|
||||
"website": "www.medreport.ee"
|
||||
},
|
||||
"helloName": "Tere, {{name}}",
|
||||
"hello": "Tere"
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"previewText": "Teie broneering on kinnitatud! - {{reservation}}",
|
||||
"subject": "Teie broneering on kinnitatud! - {{reservation}}",
|
||||
"helloName": "Tere, {{name}}!",
|
||||
"thankYou": "Täname tellimuse eest!",
|
||||
"viewOrder": "Vaata broneeringut",
|
||||
"customerSupport": "Klienditugi: "
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"previewText": "Your booking is confirmed! - {{reservation}}",
|
||||
"subject": "Your booking is confirmed! - {{reservation}}",
|
||||
"helloName": "Hello, {{name}}!",
|
||||
"thankYou": "Thank you for your order!",
|
||||
"viewOrder": "See booking",
|
||||
"customerSupport": "Customer support: "
|
||||
}
|
||||
Reference in New Issue
Block a user