diff --git a/src/Backend/AutopilotMonitor.Functions.Tests/EmailServiceTests.cs b/src/Backend/AutopilotMonitor.Functions.Tests/EmailServiceTests.cs index a4ca89a2..ae4b9d57 100644 --- a/src/Backend/AutopilotMonitor.Functions.Tests/EmailServiceTests.cs +++ b/src/Backend/AutopilotMonitor.Functions.Tests/EmailServiceTests.cs @@ -111,7 +111,7 @@ public sealed class EmailServiceTests Assert.Equal(EmailService.DefaultFromName, message.GetProperty("from_name").GetString()); Assert.Equal("it@contoso.invalid", message.GetProperty("to")[0].GetProperty("email").GetString()); Assert.Equal("to", message.GetProperty("to")[0].GetProperty("type").GetString()); - Assert.Equal(EmailTemplates.PreviewApprovedSubject, message.GetProperty("subject").GetString()); + Assert.Equal("Your Autopilot Monitor access for contoso.invalid is ready", message.GetProperty("subject").GetString()); Assert.Contains("contoso.invalid", message.GetProperty("html").GetString()); // A text part of our own (HtmlToText) instead of the provider's derived one, and the // RFC 3834 header on every automated mail. @@ -258,7 +258,8 @@ public sealed class EmailServiceTests // The mail is sent from a noreply address after portal access is gone, so the // feedback pointers must be external channels the recipient can actually reach. Assert.Contains("https://github.com/okieselbach/AutopilotMonitor/issues", html); - Assert.Contains("linkedin.com", html); + Assert.Contains("github.com/okieselbach/AutopilotMonitor/issues", html); + Assert.DoesNotContain("linkedin.com", html); // one feedback link only (user go 2026-10-08) } [Fact] diff --git a/src/Backend/AutopilotMonitor.Functions/Services/EmailService.cs b/src/Backend/AutopilotMonitor.Functions/Services/EmailService.cs index 223a9b6d..bf4ca957 100644 --- a/src/Backend/AutopilotMonitor.Functions/Services/EmailService.cs +++ b/src/Backend/AutopilotMonitor.Functions/Services/EmailService.cs @@ -96,7 +96,7 @@ public class EmailService : IEmailService, IOffboardFarewellEmailSender } var html = await _templates.GetHtmlAsync(EmailTemplateKind.Welcome, domainName, ct); - var sent = (await SendViaMandrillAsync(toEmail, EmailTemplates.PreviewApprovedSubject, html, "welcome", ct, + var sent = (await SendViaMandrillAsync(toEmail, EmailTemplates.PreviewApprovedSubjectFor(domainName), html, "welcome", ct, HtmlToText.Convert(html), AutomatedHeaders)).Accepted; if (sent) @@ -163,7 +163,7 @@ public class EmailService : IEmailService, IOffboardFarewellEmailSender ? await _templates.GetHtmlAsync(kind, domainName, ct) : EmailTemplateService.Render(draftHtml, domainName); - var sent = (await SendViaMandrillAsync(toEmail, EmailTemplateService.Subject(kind), html, "test", ct, + var sent = (await SendViaMandrillAsync(toEmail, EmailTemplateService.Subject(kind, domainName), html, "test", ct, HtmlToText.Convert(html), AutomatedHeaders)).Accepted; if (sent) _logger.LogInformation("{Kind} test email sent to {ToEmail} (draft={IsDraft})", kind, toEmail, draftHtml is not null); diff --git a/src/Backend/AutopilotMonitor.Functions/Services/EmailTemplateService.cs b/src/Backend/AutopilotMonitor.Functions/Services/EmailTemplateService.cs index fe90c330..18658439 100644 --- a/src/Backend/AutopilotMonitor.Functions/Services/EmailTemplateService.cs +++ b/src/Backend/AutopilotMonitor.Functions/Services/EmailTemplateService.cs @@ -65,8 +65,12 @@ public class EmailTemplateService : IEmailTemplateProvider public static string KindKey(EmailTemplateKind kind) => kind == EmailTemplateKind.Welcome ? "welcome" : "farewell"; - public static string Subject(EmailTemplateKind kind) => kind == EmailTemplateKind.Welcome - ? EmailTemplates.PreviewApprovedSubject + /// The subject as the editor shows it: the welcome one with the domain placeholder left in place. + public static string Subject(EmailTemplateKind kind) => Subject(kind, DomainPlaceholder); + + /// The subject a mail to carries. + public static string Subject(EmailTemplateKind kind, string? domainName) => kind == EmailTemplateKind.Welcome + ? EmailTemplates.PreviewApprovedSubjectFor(domainName) : EmailTemplates.OffboardingFarewellSubject; /// Built-in template with the placeholder left in place (for the editor). diff --git a/src/Backend/AutopilotMonitor.Functions/Services/EmailTemplates.cs b/src/Backend/AutopilotMonitor.Functions/Services/EmailTemplates.cs index 19d82abb..2c700db5 100644 --- a/src/Backend/AutopilotMonitor.Functions/Services/EmailTemplates.cs +++ b/src/Backend/AutopilotMonitor.Functions/Services/EmailTemplates.cs @@ -9,16 +9,25 @@ namespace AutopilotMonitor.Functions.Services; /// public static class EmailTemplates { - public const string PreviewApprovedSubject = "Your Autopilot Monitor access is ready!"; + /// + /// Welcome subject with the tenant's domain in place of {domain}: a concrete, calm subject + /// line (no exclamation mark, no generic "your access") is what a first mail to a new + /// organisation needs to stay out of the junk folder. + /// + public const string PreviewApprovedSubject = "Your Autopilot Monitor access for {domain} is ready"; + + public static string PreviewApprovedSubjectFor(string? domainName) + => PreviewApprovedSubject.Replace("{domain}", string.IsNullOrWhiteSpace(domainName) ? "your organization" : domainName.Trim()); // ── Offboarding farewell email ──────────────────────────────────────────── // // Sent once, after the offboarding worker finishes Phase 2 (post History terminal // write). Deliberately makes no claims about what data was or wasn't deleted — // custom rules are archived and the audit history row survives, so the only durable - // statement is "the offboarding is complete". Feedback pointers go to GitHub/LinkedIn - // (same channels as the welcome email) because this is a noreply sender and the - // in-app feedback widget is gone once the tenant is offboarded. + // statement is "the offboarding is complete". The feedback pointer goes to GitHub (same + // channel as the welcome email; a second, personal link domain only costs deliverability) + // because this is a noreply sender and the in-app feedback widget is gone once the + // tenant is offboarded. public const string OffboardingFarewellSubject = "Thank you for using Autopilot Monitor"; @@ -91,7 +100,6 @@ public static class EmailTemplates

@@ -100,7 +108,7 @@ public static class EmailTemplates

This is the last email you'll receive from us. This mailbox doesn't accept - replies – please use the links above if you'd like to get in touch. + replies – please use the link above if you'd like to get in touch.

@@ -169,10 +177,10 @@ public static class EmailTemplates -

Welcome to Autopilot Monitor!

+

Welcome to Autopilot Monitor

- Great news – access for {displayDomain} has been activated and is ready to use. + Access for {displayDomain} has been activated and is ready to use. You can now sign in and start monitoring your Windows Autopilot enrollments in real time.

@@ -195,17 +203,15 @@ public static class EmailTemplates

- Your feedback is incredibly valuable and helps shape the product. If you run into issues - or have ideas for improvements, please don't hesitate to reach out: + Questions, problems or ideas for improvements are welcome:

- Thanks for being part of the journey – enjoy Autopilot Monitor! + Thank you for using Autopilot Monitor.