mirror of
https://github.com/open-reception/appointment-booking-software.git
synced 2026-09-08 10:17:39 +02:00
Added poc of new email renderer
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
<script lang="js">
|
||||
import { m } from "$i18n/messages";
|
||||
import { getLocale, setLocale } from "$i18n/runtime";
|
||||
import EmailButton from "./components/EmailButton.svelte";
|
||||
import EmailLayout from "./components/EmailLayout.svelte";
|
||||
import EmailText from "./components/EmailText.svelte";
|
||||
|
||||
let { name, locale } = $props();
|
||||
|
||||
setLocale(locale);
|
||||
console.log("Locale in AppointmentBooked:", getLocale());
|
||||
</script>
|
||||
|
||||
<EmailLayout>
|
||||
<p>{m["absences.add.description"]()} {name},</p>
|
||||
<p>your appointment has been booked.</p>
|
||||
<h2>Vaccination Appointment</h2>
|
||||
<p>
|
||||
Dr. John Doe<br />
|
||||
June 30, 2024<br />
|
||||
10:00 AM - 10:15 AM
|
||||
</p>
|
||||
<h2>Praxis Dr. Jane Doe</h2>
|
||||
<p>
|
||||
Musterstraße 10<br />
|
||||
Etage 4<br />
|
||||
12345 Musterstadt
|
||||
</p>
|
||||
<EmailButton href="https://open-reception.org/appointments">View Appointments</EmailButton>
|
||||
<EmailText variant="md" color="text-light">
|
||||
You are receiving this email because someone booked an appointment with your e-mail address.
|
||||
</EmailText>
|
||||
</EmailLayout>
|
||||
@@ -0,0 +1,31 @@
|
||||
<script lang="js">
|
||||
let { children, href } = $props();
|
||||
</script>
|
||||
|
||||
<div class="button-container">
|
||||
<a class="button" {href}>{@render children?.()}</a>
|
||||
</div>
|
||||
|
||||
<svelte:head>
|
||||
<style>
|
||||
.button-container {
|
||||
margin: 24px 0;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
.button {
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
color: #ffffff;
|
||||
background-color: #000000;
|
||||
padding: 12px 32px;
|
||||
padding: 0.75rem 2rem;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
border-radius: 12px;
|
||||
}
|
||||
.button:hover {
|
||||
background-color: #333333;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
</svelte:head>
|
||||
@@ -0,0 +1,88 @@
|
||||
<script lang="js">
|
||||
import EmailText from "./EmailText.svelte";
|
||||
|
||||
let { children } = $props();
|
||||
</script>
|
||||
|
||||
<div class="page">
|
||||
<div class="container">
|
||||
<div class="paper">
|
||||
<div class="content">
|
||||
{@render children?.()}
|
||||
</div>
|
||||
</div>
|
||||
<div class="branding">
|
||||
<EmailText variant="sm" color="text-light">
|
||||
Powered by <a href="https://open-reception.org">OpenReception</a>
|
||||
</EmailText>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<svelte:head>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
min-height: 100%;
|
||||
min-height: 100vh;
|
||||
box-sizing: border-box;
|
||||
font-family: "Inter", Arial, sans-serif;
|
||||
}
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: underline;
|
||||
}
|
||||
h1 {
|
||||
font-size: 18px;
|
||||
line-height: 1.5;
|
||||
font-weight: 600;
|
||||
}
|
||||
h2 {
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
font-weight: 600;
|
||||
margin-top: 24px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
h3 {
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
font-weight: 500;
|
||||
}
|
||||
h2 + p {
|
||||
margin-top: 0;
|
||||
}
|
||||
.page {
|
||||
background-color: #f6f6f6;
|
||||
min-height: 100%;
|
||||
min-height: 100vh;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
padding: 0 16px 16px;
|
||||
padding: 0 1rem 1rem;
|
||||
}
|
||||
.container {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.content {
|
||||
padding: 24px;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
.paper {
|
||||
background-color: white;
|
||||
width: 100%;
|
||||
box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
border-bottom-right-radius: 6px;
|
||||
border-bottom-left-radius: 6px;
|
||||
}
|
||||
.branding {
|
||||
margin: 16px 0;
|
||||
margin: 1rem 0;
|
||||
padding: 0 16px;
|
||||
padding: 0 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</svelte:head>
|
||||
@@ -0,0 +1,29 @@
|
||||
<script lang="js">
|
||||
let { children, variant, color } = $props();
|
||||
</script>
|
||||
|
||||
<span class={[variant, color].join(" ")}>{@render children?.()}</span>
|
||||
|
||||
<svelte:head>
|
||||
<style>
|
||||
.lg {
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.md {
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.sm {
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.xs {
|
||||
font-size: 10px;
|
||||
line-height: 1.25;
|
||||
}
|
||||
.text-light {
|
||||
color: #888888;
|
||||
}
|
||||
</style>
|
||||
</svelte:head>
|
||||
@@ -0,0 +1,21 @@
|
||||
// is not exported from svelte
|
||||
export type RenderOutput = {
|
||||
head: string;
|
||||
body: string;
|
||||
};
|
||||
|
||||
export const renderOutputToHtml = (renderOutput: RenderOutput) => {
|
||||
return `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
${renderOutput.head}
|
||||
</head>
|
||||
<body>
|
||||
${renderOutput.body}
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
import { dev } from "$app/environment";
|
||||
import type { ParamMatcher } from "@sveltejs/kit";
|
||||
|
||||
export const match: ParamMatcher = (param) => {
|
||||
// Doesn't have to be include, but needs to return a boolean
|
||||
return dev && param.includes("local-only");
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
import AppointmentBooked from "$lib/emails/AppointmentBooked.svelte";
|
||||
import { renderOutputToHtml } from "$lib/emails/utils";
|
||||
import type { RequestHandler } from "@sveltejs/kit";
|
||||
import { render } from "svelte/server";
|
||||
|
||||
export const GET: RequestHandler = async () => {
|
||||
const emailRender = render(AppointmentBooked, { props: { name: "John Doe", locale: "de" } });
|
||||
|
||||
return new Response(renderOutputToHtml(emailRender), {
|
||||
headers: {
|
||||
"Content-Type": "text/html",
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -11,6 +11,12 @@ import type { Handle } from "@sveltejs/kit";
|
||||
* @returns {Promise<Response>} The response with applied headers and rate limiting
|
||||
*/
|
||||
export const i18nHandle: Handle = ({ event, resolve }) => {
|
||||
// Disable i18n for API and local-only routes
|
||||
// Avoids using i18n middleware where emails are rendered
|
||||
if (event.url.pathname.startsWith("/api") || event.url.pathname.startsWith("/local-only")) {
|
||||
return resolve(event);
|
||||
}
|
||||
|
||||
// Determine browser locale and use it
|
||||
const cookieLocale = event.cookies.get(cookieName);
|
||||
let locale: Locale | undefined = cookieLocale as Locale;
|
||||
|
||||
Reference in New Issue
Block a user