+
- | ID Document CV |
- {{cvDocumentId}} |
+ ID Document CV |
+ {{cvDocumentId}} |
- | Job |
- {{jobLabel}} |
+ Job |
+ {{jobLabel}} |
- | URL |
- {{jobUrl}} |
+ URL |
+ {{jobUrl}} |
- | Scor |
- {{score}}% |
+ Scor |
+ {{score}}% |
Rezumat
@@ -108,13 +108,25 @@ namespace Email.Data.Migrations
Row("email.match.job-search-footer", "en",
@"
Want to find more jobs matching your CV?
-Search Jobs
+
(link valid for {{expiryDays}} days)
",
"Job search CTA appended to match result email (HTML formatted)");
Row("email.match.job-search-footer", "ro",
@"
Vrei să găsești mai multe joburi potrivite CV-ului tău?
-Caută Joburi
+
(link valabil {{expiryDays}} zile)
",
"CTA cautare joburi adaugat la emailul de potrivire CV (format HTML)");
@@ -126,34 +138,84 @@ namespace Email.Data.Migrations
Row("email.search-results.body", "en",
@"Job Search Results
MyAi.ro found {{count}} jobs matching your CV:
-
-{{items}}
-
",
+{{items}}",
"Body preamble for job search results email (HTML formatted)");
Row("email.search-results.body", "ro",
@"Rezultate Căutare Joburi
MyAi.ro a găsit {{count}} joburi potrivite CV-ului tău:
-
-{{items}}
-
",
+{{items}}",
"Corpul emailului de rezultate cautare joburi (format HTML)");
+ // Job search results email — scan summary block (keywords + providers used)
+ Row("email.search-results.scan-summary", "en",
+ @"
+
+ |
+ Keywords used: {{keywordsHtml}}
+ Providers scanned: {{providers}}
+ |
+
+
",
+ "Scan summary block prepended to job search results email (HTML formatted)");
+ Row("email.search-results.scan-summary", "ro",
+ @"
+
+ |
+ Cuvinte cheie folosite: {{keywordsHtml}}
+ Furnizori scanați: {{providers}}
+ |
+
+
",
+ "Bloc rezumat scanare adaugat la emailul de rezultate cautare joburi (format HTML)");
+
+ // Job search results email — single job result item card
+ Row("email.search-results.item", "en",
+ @"
+
+
+ {{index}}. {{jobTitle}}
+ {{score}}% match
+ [{{providerName}}]
+ {{jobUrl}}
+ {{summary}}
+ |
+
+
",
+ "Single job result card in job search results email (HTML formatted)");
+ Row("email.search-results.item", "ro",
+ @"
+
+
+ {{index}}. {{jobTitle}}
+ {{score}}% potrivire
+ [{{providerName}}]
+ {{jobUrl}}
+ {{summary}}
+ |
+
+
",
+ "Card job individual in emailul de rezultate cautare joburi (format HTML)");
+
// Job search results email — no results found - HTML formatted
Row("email.search-results.empty", "en",
- @"
-
-No jobs found
-MyAi.ro found no jobs matching your CV at this moment. Please try again later or update your CV to improve your match results.
-
-
",
+ @"
+
+ |
+ No jobs found
+ MyAi.ro found no jobs matching your CV at this moment. Please try again later or update your CV to improve your match results.
+ |
+
+
",
"No results message for job search results email (HTML formatted)");
Row("email.search-results.empty", "ro",
- @"
-
-Niciun job găsit
-MyAi.ro nu a găsit joburi potrivite CV-ului tău în acest moment. Te rugăm să încerci din nou mai târziu sau să-ți actualizezi CV-ul pentru a obține rezultate mai bune.
-
-
",
+ @"
+
+ |
+ Niciun job găsit
+ MyAi.ro nu a găsit joburi potrivite CV-ului tău în acest moment. Te rugăm să încerci din nou mai târziu sau să-ți actualizezi CV-ul pentru a obține rezultate mai bune.
+ |
+
+
",
"Mesaj fara rezultate pentru emailul de cautare joburi (format HTML)");
// HTML job-search start page messages
diff --git a/Jobs/cv-search-job/Services/CvSearchEmailSender.cs b/Jobs/cv-search-job/Services/CvSearchEmailSender.cs
index 3262d8e..08fac2b 100644
--- a/Jobs/cv-search-job/Services/CvSearchEmailSender.cs
+++ b/Jobs/cv-search-job/Services/CvSearchEmailSender.cs
@@ -89,7 +89,7 @@ public sealed class CvSearchEmailSender
///
private string BuildBody(IReadOnlyList results, IReadOnlyList keywords, IReadOnlyList providerNames, string language)
{
- var scanSummary = BuildScanSummary(keywords, providerNames);
+ var scanSummary = BuildScanSummary(keywords, providerNames, language);
if (results.Count == 0)
return scanSummary + _emailTemplates.Get("email.search-results.empty", language);
@@ -98,18 +98,18 @@ public sealed class CvSearchEmailSender
for (int i = 0; i < results.Count; i++)
{
var r = results[i];
- var matchResp = TryParseResult(r.ResultJson);
- var summary = matchResp?.Summary;
+ var summary = TryParseResult(r.ResultJson)?.Summary;
+ var summaryHtml = string.IsNullOrWhiteSpace(summary)
+ ? ""
+ : $"{summary}
";
- items.Append($"""
-
-
{i + 1}. {r.JobTitle}
-
{r.Score}% match
-
[{r.ProviderName}]
-
{r.JobUrl}
- {(string.IsNullOrWhiteSpace(summary) ? "" : $"
{summary}
")}
-
- """);
+ items.Append(_emailTemplates.Render("email.search-results.item", language,
+ ("index", (i + 1).ToString()),
+ ("jobTitle", r.JobTitle),
+ ("score", r.Score.ToString()),
+ ("providerName", r.ProviderName),
+ ("jobUrl", r.JobUrl),
+ ("summary", summaryHtml)));
}
return _emailTemplates.Render("email.search-results.body", language,
@@ -118,25 +118,23 @@ public sealed class CvSearchEmailSender
}
///
- /// Builds the scan summary block showing the CV keywords and providers used for the search.
+ /// Renders the scan summary block via template, passing keyword tags and provider list as data.
+ /// Keyword tags are built here because they are variable-count inline elements, not structural HTML.
///
- private static string BuildScanSummary(IReadOnlyList keywords, IReadOnlyList providerNames)
+ private string BuildScanSummary(IReadOnlyList keywords, IReadOnlyList providerNames, string language)
{
var keywordsHtml = keywords.Count > 0
? string.Join(" ", keywords.Select(k =>
- $"{k}"))
- : "none detected";
+ $"{k}"))
+ : "none detected";
- var providersText = providerNames.Count > 0
+ var providers = providerNames.Count > 0
? string.Join(", ", providerNames)
: "none";
- return $"""
-
-
Keywords used: {keywordsHtml}
-
Providers scanned: {providersText}
-
- """;
+ return _emailTemplates.Render("email.search-results.scan-summary", language,
+ ("keywordsHtml", keywordsHtml),
+ ("providers", providers));
}
///