mirror of
https://github.com/open-reception/appointment-booking-software.git
synced 2026-08-17 21:25:52 +02:00
Merge pull request #312 from open-reception/fix/emails-always-in-proper-language
Fix: E-Mails are always using recipient locale now
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { m } from "$i18n/messages";
|
||||
import { setLocale } from "$i18n/runtime";
|
||||
import type { SupportedLocale } from "$lib/const/locales";
|
||||
import type { SelectTenant } from "$lib/server/db/central-schema";
|
||||
import { type SelectAppointment } from "$lib/server/db/tenant-schema";
|
||||
@@ -34,16 +33,12 @@
|
||||
};
|
||||
cancelUrl: string;
|
||||
} = $props();
|
||||
|
||||
$effect(() => {
|
||||
setLocale(locale);
|
||||
});
|
||||
</script>
|
||||
|
||||
<EmailLayout>
|
||||
<EmailText variant="md">{m["emails.greeting"]({ name: user.email })}</EmailText>
|
||||
<EmailLayout {locale}>
|
||||
<EmailText variant="md">{m["emails.greeting"]({ name: user.email }, { locale })}</EmailText>
|
||||
<EmailText variant="md">
|
||||
{m["emails.appointmentBooked.introduction"]()}
|
||||
{m["emails.appointmentBooked.introduction"]({}, { locale })}
|
||||
</EmailText>
|
||||
<EmailHeadline>{channel}</EmailHeadline>
|
||||
<EmailText variant="md">
|
||||
@@ -59,8 +54,8 @@
|
||||
{address.zip}
|
||||
{address.city}
|
||||
</EmailText>
|
||||
<EmailButton href={cancelUrl}>{m["emails.appointmentBooked.action"]()}</EmailButton>
|
||||
<EmailButton href={cancelUrl}>{m["emails.appointmentBooked.action"]({}, { locale })}</EmailButton>
|
||||
<EmailText variant="md" color="text-light">
|
||||
{m["emails.appointmentBooked.reason"]()}
|
||||
{m["emails.appointmentBooked.reason"]({}, { locale })}
|
||||
</EmailText>
|
||||
</EmailLayout>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { m } from "$i18n/messages";
|
||||
import { setLocale } from "$i18n/runtime";
|
||||
import type { SupportedLocale } from "$lib/const/locales";
|
||||
import type { SelectTenant } from "$lib/server/db/central-schema";
|
||||
import { type SelectAppointment } from "$lib/server/db/tenant-schema";
|
||||
@@ -31,16 +30,12 @@
|
||||
city: string;
|
||||
};
|
||||
} = $props();
|
||||
|
||||
$effect(() => {
|
||||
setLocale(locale);
|
||||
});
|
||||
</script>
|
||||
|
||||
<EmailLayout>
|
||||
<EmailText variant="md">{m["emails.greeting"]({ name: user.email })}</EmailText>
|
||||
<EmailLayout {locale}>
|
||||
<EmailText variant="md">{m["emails.greeting"]({ name: user.email }, { locale })}</EmailText>
|
||||
<EmailText variant="md">
|
||||
{m["emails.appointmentCancelled.introduction"]()}
|
||||
{m["emails.appointmentCancelled.introduction"]({}, { locale })}
|
||||
</EmailText>
|
||||
<EmailHeadline>{channel}</EmailHeadline>
|
||||
<EmailText variant="md">
|
||||
@@ -57,6 +52,6 @@
|
||||
{address.city}
|
||||
</EmailText>
|
||||
<EmailText variant="md" color="text-light">
|
||||
{m["emails.appointmentCancelled.reason"]()}
|
||||
{m["emails.appointmentCancelled.reason"]({}, { locale })}
|
||||
</EmailText>
|
||||
</EmailLayout>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { m } from "$i18n/messages";
|
||||
import { setLocale } from "$i18n/runtime";
|
||||
import type { SupportedLocale } from "$lib/const/locales";
|
||||
import type { SelectTenant } from "$lib/server/db/central-schema";
|
||||
import { type SelectAppointment } from "$lib/server/db/tenant-schema";
|
||||
@@ -31,16 +30,12 @@
|
||||
city: string;
|
||||
};
|
||||
} = $props();
|
||||
|
||||
$effect(() => {
|
||||
setLocale(locale);
|
||||
});
|
||||
</script>
|
||||
|
||||
<EmailLayout>
|
||||
<EmailText variant="md">{m["emails.greeting"]({ name: user.email })}</EmailText>
|
||||
<EmailLayout {locale}>
|
||||
<EmailText variant="md">{m["emails.greeting"]({ name: user.email }, { locale })}</EmailText>
|
||||
<EmailText variant="md">
|
||||
{m["emails.appointmentRejected.introduction"]()}
|
||||
{m["emails.appointmentRejected.introduction"]({}, { locale })}
|
||||
</EmailText>
|
||||
<EmailHeadline>{channel}</EmailHeadline>
|
||||
<EmailText variant="md">
|
||||
@@ -57,6 +52,6 @@
|
||||
{address.city}
|
||||
</EmailText>
|
||||
<EmailText variant="md" color="text-light">
|
||||
{m["emails.appointmentRejected.reason"]()}
|
||||
{m["emails.appointmentRejected.reason"]({}, { locale })}
|
||||
</EmailText>
|
||||
</EmailLayout>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { m } from "$i18n/messages";
|
||||
import { setLocale } from "$i18n/runtime";
|
||||
import type { SupportedLocale } from "$lib/const/locales";
|
||||
import type { SelectTenant } from "$lib/server/db/central-schema";
|
||||
import { type SelectAppointment } from "$lib/server/db/tenant-schema";
|
||||
@@ -34,16 +33,12 @@
|
||||
};
|
||||
cancelUrl: string;
|
||||
} = $props();
|
||||
|
||||
$effect(() => {
|
||||
setLocale(locale);
|
||||
});
|
||||
</script>
|
||||
|
||||
<EmailLayout>
|
||||
<EmailText variant="md">{m["emails.greeting"]({ name: user.email })}</EmailText>
|
||||
<EmailLayout {locale}>
|
||||
<EmailText variant="md">{m["emails.greeting"]({ name: user.email }, { locale })}</EmailText>
|
||||
<EmailText variant="md">
|
||||
{m["emails.appointmentReminder.introduction"]({ tenant: tenant.longName })}
|
||||
{m["emails.appointmentReminder.introduction"]({ tenant: tenant.longName }, { locale })}
|
||||
</EmailText>
|
||||
<EmailHeadline>{channel}</EmailHeadline>
|
||||
<EmailText variant="md">
|
||||
@@ -59,8 +54,10 @@
|
||||
{address.zip}
|
||||
{address.city}
|
||||
</EmailText>
|
||||
<EmailButton href={cancelUrl}>{m["emails.appointmentReminder.action"]()}</EmailButton>
|
||||
<EmailButton href={cancelUrl}>
|
||||
{m["emails.appointmentReminder.action"]({}, { locale })}
|
||||
</EmailButton>
|
||||
<EmailText variant="md" color="text-light">
|
||||
{m["emails.appointmentReminder.reason"]()}
|
||||
{m["emails.appointmentReminder.reason"]({}, { locale })}
|
||||
</EmailText>
|
||||
</EmailLayout>
|
||||
|
||||
@@ -37,10 +37,10 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<EmailLayout>
|
||||
<EmailText variant="md">{m["emails.greeting"]({ name: user.email })}</EmailText>
|
||||
<EmailLayout {locale}>
|
||||
<EmailText variant="md">{m["emails.greeting"]({ name: user.email }, { locale })}</EmailText>
|
||||
<EmailText variant="md">
|
||||
{m["emails.appointmentRequest.introduction"]()}
|
||||
{m["emails.appointmentRequest.introduction"]({}, { locale })}
|
||||
</EmailText>
|
||||
<EmailHeadline>{channel}</EmailHeadline>
|
||||
<EmailText variant="md">
|
||||
@@ -57,6 +57,6 @@
|
||||
{address.city}
|
||||
</EmailText>
|
||||
<EmailText variant="md" color="text-light">
|
||||
{m["emails.appointmentRequest.reason"]()}
|
||||
{m["emails.appointmentRequest.reason"]({}, { locale })}
|
||||
</EmailText>
|
||||
</EmailLayout>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { m } from "$i18n/messages";
|
||||
import { setLocale } from "$i18n/runtime";
|
||||
import type { SupportedLocale } from "$lib/const/locales";
|
||||
import type { SelectUserEmail } from "$lib/server/email/email-service";
|
||||
import EmailButton from "./components/EmailButton.svelte";
|
||||
@@ -18,22 +17,22 @@
|
||||
confirmUrl: string;
|
||||
expirationMinutes: number;
|
||||
} = $props();
|
||||
|
||||
$effect(() => {
|
||||
setLocale(locale);
|
||||
});
|
||||
</script>
|
||||
|
||||
<EmailLayout>
|
||||
<EmailText variant="md">{m["emails.greeting"]({ name: user.name })}</EmailText>
|
||||
<EmailLayout {locale}>
|
||||
<EmailText variant="md">
|
||||
{m["emails.confirmation.introduction"]()}
|
||||
{m["emails.greeting"]({ name: user.name }, { locale })}
|
||||
</EmailText>
|
||||
<EmailButton href={confirmUrl}>{m["emails.confirmation.action"]()}</EmailButton>
|
||||
<EmailText variant="md">
|
||||
{m["emails.confirmation.hint"]({ expirationMinutes })}
|
||||
{m["emails.confirmation.introduction"]({}, { locale })}
|
||||
</EmailText>
|
||||
<EmailButton {locale} href={confirmUrl}>
|
||||
{m["emails.confirmation.action"]({}, { locale })}
|
||||
</EmailButton>
|
||||
<EmailText variant="md">
|
||||
{m["emails.confirmation.hint"]({ expirationMinutes }, { locale })}
|
||||
</EmailText>
|
||||
<EmailText variant="md" color="text-light">
|
||||
{m["emails.confirmation.reason"]()}
|
||||
{m["emails.confirmation.reason"]({}, { locale })}
|
||||
</EmailText>
|
||||
</EmailLayout>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { m } from "$i18n/messages";
|
||||
import { setLocale } from "$i18n/runtime";
|
||||
import type { SupportedLocale } from "$lib/const/locales";
|
||||
import type { SelectUserEmail } from "$lib/server/email/email-service";
|
||||
import EmailButton from "./components/EmailButton.svelte";
|
||||
@@ -16,19 +15,15 @@
|
||||
user: SelectUserEmail;
|
||||
dashboardUrl: string;
|
||||
} = $props();
|
||||
|
||||
$effect(() => {
|
||||
setLocale(locale);
|
||||
});
|
||||
</script>
|
||||
|
||||
<EmailLayout>
|
||||
<EmailText variant="md">{m["emails.greeting"]({ name: user.name })}</EmailText>
|
||||
<EmailLayout {locale}>
|
||||
<EmailText variant="md">{m["emails.greeting"]({ name: user.name }, { locale })}</EmailText>
|
||||
<EmailText variant="md">
|
||||
{m["emails.notification.introduction"]()}
|
||||
{m["emails.notification.introduction"]({}, { locale })}
|
||||
</EmailText>
|
||||
<EmailButton href={dashboardUrl}>{m["emails.notification.action"]()}</EmailButton>
|
||||
<EmailButton href={dashboardUrl}>{m["emails.notification.action"]({}, { locale })}</EmailButton>
|
||||
<EmailText variant="md" color="text-light">
|
||||
{m["emails.notification.reason"]()}
|
||||
{m["emails.notification.reason"]({}, { locale })}
|
||||
</EmailText>
|
||||
</EmailLayout>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { m } from "$i18n/messages";
|
||||
import { setLocale } from "$i18n/runtime";
|
||||
import type { SupportedLocale } from "$lib/const/locales";
|
||||
import { type SelectTenant } from "$lib/server/db/central-schema";
|
||||
import type { SelectClient } from "$lib/server/email/email-service";
|
||||
@@ -19,19 +18,15 @@
|
||||
tenant: SelectTenant;
|
||||
loginUrl: string;
|
||||
} = $props();
|
||||
|
||||
$effect(() => {
|
||||
setLocale(locale);
|
||||
});
|
||||
</script>
|
||||
|
||||
<EmailLayout>
|
||||
<EmailText variant="md">{m["emails.greeting"]({ name: user.email })}</EmailText>
|
||||
<EmailLayout {locale}>
|
||||
<EmailText variant="md">{m["emails.greeting"]({ name: user.email }, { locale })}</EmailText>
|
||||
<EmailText variant="md">
|
||||
{m["emails.pinReset.introduction"]({ tenant: tenant.longName })}
|
||||
{m["emails.pinReset.introduction"]({ tenant: tenant.longName }, { locale })}
|
||||
</EmailText>
|
||||
<EmailButton href={loginUrl}>{m["emails.pinReset.action"]()}</EmailButton>
|
||||
<EmailButton href={loginUrl}>{m["emails.pinReset.action"]({}, { locale })}</EmailButton>
|
||||
<EmailText variant="md" color="text-light">
|
||||
{m["emails.pinReset.reason"]()}
|
||||
{m["emails.pinReset.reason"]({}, { locale })}
|
||||
</EmailText>
|
||||
</EmailLayout>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script lang="js">
|
||||
<script lang="ts">
|
||||
let { children, variant = "h2", class: className = "" } = $props();
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
<script lang="js">
|
||||
<script lang="ts">
|
||||
import { m } from "$i18n/messages";
|
||||
import type { SupportedLocale } from "$lib/const/locales";
|
||||
import type { Snippet } from "svelte";
|
||||
import EmailText from "./EmailText.svelte";
|
||||
|
||||
let { children } = $props();
|
||||
let {
|
||||
locale,
|
||||
children,
|
||||
}: {
|
||||
locale: SupportedLocale;
|
||||
children?: Snippet;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<div class="page">
|
||||
@@ -11,7 +19,7 @@
|
||||
{@render children?.()}
|
||||
</div>
|
||||
<EmailText variant="sm" color="text-light" class="branding">
|
||||
{m["emails.poweredBy"]()} <a href="https://open-reception.org">OpenReception</a>
|
||||
{m["emails.poweredBy"]({}, { locale })} <a href="https://open-reception.org">OpenReception</a>
|
||||
</EmailText>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script lang="js">
|
||||
<script lang="ts">
|
||||
let { children, variant, color = "text-darkest", class: className = "" } = $props();
|
||||
</script>
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ import type { SelectTenant, SelectUser } from "$lib/server/db/central-schema";
|
||||
import { getTenantDb } from "$lib/server/db";
|
||||
import * as tenantSchema from "$lib/server/db/tenant-schema";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { setLocale } from "$i18n/runtime";
|
||||
import { m } from "$i18n/messages";
|
||||
import { render } from "svelte/server";
|
||||
import AppointmentBooked from "$lib/emails/AppointmentBooked.svelte";
|
||||
@@ -139,9 +138,12 @@ export async function sendPinResetEmail(
|
||||
const recipient = createEmailRecipient(user);
|
||||
const locale = (recipient.language as Language) || "en";
|
||||
|
||||
const subject = m["emails.pinReset.subject"]({
|
||||
tenant: tenant.longName,
|
||||
});
|
||||
const subject = m["emails.pinReset.subject"](
|
||||
{
|
||||
tenant: tenant.longName,
|
||||
},
|
||||
{ locale },
|
||||
);
|
||||
const emailRender = render(PinReset, {
|
||||
props: {
|
||||
locale,
|
||||
@@ -193,9 +195,12 @@ export async function sendAppointmentReminderEmail(
|
||||
const { recipient, locale } = await getRecipient(user);
|
||||
const channelTitle = await getChannelTitle(tenant.id, appointment.channelId, locale);
|
||||
// Generate email
|
||||
const subject = m["emails.appointmentReminder.subject"]({
|
||||
tenant: tenant.longName,
|
||||
});
|
||||
const subject = m["emails.appointmentReminder.subject"](
|
||||
{
|
||||
tenant: tenant.longName,
|
||||
},
|
||||
{ locale },
|
||||
);
|
||||
const emailRender = render(AppointmentBooked, {
|
||||
props: {
|
||||
locale,
|
||||
@@ -219,7 +224,6 @@ const getRecipient = async (user: SelectClient | SelectUser) => {
|
||||
? { email: user.email, language: user.language }
|
||||
: createEmailRecipient(user);
|
||||
const locale = (recipient.language as Language) || "en";
|
||||
setLocale(locale);
|
||||
return { recipient, locale };
|
||||
};
|
||||
|
||||
@@ -259,10 +263,13 @@ export async function sendAppointmentRejectedEmail(
|
||||
const { recipient, locale } = await getRecipient(user);
|
||||
|
||||
// Generate email
|
||||
const subject = m["emails.appointmentRejected.subject"]({
|
||||
channel: channelTitle || appointment.channelId,
|
||||
tenant: tenant.longName,
|
||||
});
|
||||
const subject = m["emails.appointmentRejected.subject"](
|
||||
{
|
||||
channel: channelTitle || appointment.channelId,
|
||||
tenant: tenant.longName,
|
||||
},
|
||||
{ locale },
|
||||
);
|
||||
const emailRender = render(AppointmentRejected, {
|
||||
props: {
|
||||
locale,
|
||||
@@ -303,10 +310,13 @@ export async function sendAppointmentCreatedEmail(
|
||||
const { recipient, locale } = await getRecipient(user);
|
||||
|
||||
// Generate email
|
||||
const subject = m["emails.appointmentBooked.subject"]({
|
||||
channel: channelTitle || appointment.channelId,
|
||||
tenant: tenant.longName,
|
||||
});
|
||||
const subject = m["emails.appointmentBooked.subject"](
|
||||
{
|
||||
channel: channelTitle || appointment.channelId,
|
||||
tenant: tenant.longName,
|
||||
},
|
||||
{ locale },
|
||||
);
|
||||
const emailRender = render(AppointmentBooked, {
|
||||
props: {
|
||||
locale,
|
||||
@@ -344,10 +354,13 @@ export async function sendAppointmentRequestEmail(
|
||||
const agent = await agentService.getAgentById(appointment.agentId);
|
||||
const { recipient, locale } = await getRecipient(user);
|
||||
// Generate email
|
||||
const subject = m["emails.appointmentRequest.subject"]({
|
||||
channel: channelTitle || appointment.channelId,
|
||||
tenant: tenant.longName,
|
||||
});
|
||||
const subject = m["emails.appointmentRequest.subject"](
|
||||
{
|
||||
channel: channelTitle || appointment.channelId,
|
||||
tenant: tenant.longName,
|
||||
},
|
||||
{ locale },
|
||||
);
|
||||
const emailRender = render(AppointmentRequest, {
|
||||
props: {
|
||||
locale,
|
||||
@@ -364,35 +377,6 @@ export async function sendAppointmentRequestEmail(
|
||||
await sendEmail(recipient, subject, html, text, tenant.longName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send appointment update notification email
|
||||
* @param {SelectClient | SelectUser} user - Database user object
|
||||
* @param {SelectTenant} tenant - Tenant information for branding
|
||||
* @param {SelectAppointment} appointment - Updated appointment details
|
||||
* @param {string} [channelTitle] - Optional channel title/name
|
||||
* @param {string} [cancelUrl] - Optional URL to cancel appointment
|
||||
* @throws {Error} When email sending fails
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function sendAppointmentUpdatedEmail(
|
||||
user: SelectClient | SelectUser,
|
||||
tenant: SelectTenant,
|
||||
appointment: SelectAppointment,
|
||||
channelTitle?: string,
|
||||
cancelUrl?: string,
|
||||
): Promise<void> {
|
||||
const recipient = createEmailRecipient(user);
|
||||
const language = (recipient.language as Language) || "en";
|
||||
const subject = language === "en" ? "Appointment Updated" : "Termin aktualisiert";
|
||||
|
||||
await sendTemplatedEmail("appointment-updated", recipient, subject, language, tenant, {
|
||||
appointment,
|
||||
appointmentDate: appointment.appointmentDate,
|
||||
title: channelTitle || appointment.channelId,
|
||||
cancelUrl,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate base URL for email templates based on request URL and tenant
|
||||
* @param {URL} requestUrl - The request URL
|
||||
@@ -427,13 +411,17 @@ export async function sendConfirmationEmail(
|
||||
const baseUrl = generateBaseUrl(requestUrl);
|
||||
const confirmUrl = `${baseUrl}/confirm/${confirmationCode}`;
|
||||
const recipient = user;
|
||||
const locale = (user.language as Language) ?? "en";
|
||||
// Generate email
|
||||
const subject = m["emails.confirmation.subject"]({
|
||||
tenant: tenant.longName,
|
||||
});
|
||||
const subject = m["emails.confirmation.subject"](
|
||||
{
|
||||
tenant: tenant.longName,
|
||||
},
|
||||
{ locale },
|
||||
);
|
||||
const emailRender = render(Confirmation, {
|
||||
props: {
|
||||
locale: (user.language as Language) ?? "en",
|
||||
locale,
|
||||
user: user as SelectUserEmail,
|
||||
confirmUrl,
|
||||
expirationMinutes,
|
||||
@@ -464,10 +452,13 @@ export async function sendAppointmentCancelledEmail(
|
||||
// Create recipient directly for SelectClient type, use helper for SelectUser
|
||||
const { recipient, locale } = await getRecipient(user);
|
||||
// Generate email
|
||||
const subject = m["emails.appointmentCancelled.subject"]({
|
||||
channel: channelTitle || appointment.channelId,
|
||||
tenant: tenant.longName,
|
||||
});
|
||||
const subject = m["emails.appointmentCancelled.subject"](
|
||||
{
|
||||
channel: channelTitle || appointment.channelId,
|
||||
tenant: tenant.longName,
|
||||
},
|
||||
{ locale },
|
||||
);
|
||||
const emailRender = render(AppointmentCancelled, {
|
||||
props: {
|
||||
locale,
|
||||
@@ -499,7 +490,7 @@ export async function sendNotificationEmail(
|
||||
// Create recipient directly for SelectClient type, use helper for SelectUser
|
||||
const { recipient, locale } = await getRecipient(user);
|
||||
// Generate email
|
||||
const subject = m["emails.notification.subject"]();
|
||||
const subject = m["emails.notification.subject"]({}, { locale });
|
||||
const emailRender = render(Notification, {
|
||||
props: {
|
||||
locale,
|
||||
|
||||
Reference in New Issue
Block a user