refactor(cv): centralize tailored templates
CI and Deploy / test (push) Successful in 5m35s
CI and Deploy / deploy (push) Successful in 2m6s

This commit is contained in:
cesnimda
2026-08-27 20:47:59 +02:00
parent 6b3acf7b07
commit 7c4e6fb5b7
6 changed files with 101 additions and 83 deletions
+6 -37
View File
@@ -16,7 +16,7 @@ public sealed class CvTemplateRenderer : ICvTemplateRenderer
public TailoredCvRenderResult Render(TailoredCvDocument? document, string? templateId, string candidateName, string jobTitle, string? companyName, string? photoDataUrl = null)
{
var normalized = TailoredCvDraftJson.Normalize(document);
var effectiveTemplateId = NormalizeTemplateId(templateId ?? normalized.TemplateId);
var effectiveTemplateId = TailoredCvTemplateCatalog.NormalizeId(templateId ?? normalized.TemplateId);
normalized.TemplateId = effectiveTemplateId;
var suggestedFileName = Slugify($"{candidateName}-{jobTitle}-{effectiveTemplateId}") + ".pdf";
var html = effectiveTemplateId switch
@@ -31,25 +31,9 @@ public sealed class CvTemplateRenderer : ICvTemplateRenderer
return new TailoredCvRenderResult(effectiveTemplateId, suggestedFileName, html);
}
private static string NormalizeTemplateId(string? value)
{
var normalized = (value ?? string.Empty).Trim().ToLowerInvariant();
return normalized switch
{
"base" => "ats-minimal",
"legacy-text" => "ats-minimal",
"harvard" => "harvard",
"auckland" => "auckland",
"edinburgh" => "edinburgh",
"monarch" => "monarch",
"fjord" => "fjord",
_ => "ats-minimal"
};
}
private static string RenderAtsMinimal(TailoredCvDocument document, string candidateName, string jobTitle, string? companyName, string? photoDataUrl)
{
var accent = ResolveAccent(document.RenderOptions.AccentColor);
var accent = TailoredCvTemplateCatalog.ResolveAccent(document.RenderOptions.AccentColor);
var showPhoto = document.RenderOptions.ShowPhoto && !string.IsNullOrWhiteSpace(photoDataUrl);
var body = RenderMainSections(document, accent, headingStyle: "caps-rule");
var companyFocusMarkup = string.IsNullOrWhiteSpace(companyName) ? string.Empty : $"<span>Company focus: {Encode(companyName)}</span>";
@@ -93,7 +77,7 @@ public sealed class CvTemplateRenderer : ICvTemplateRenderer
private static string RenderHarvard(TailoredCvDocument document, string candidateName, string jobTitle, string? companyName)
{
var accent = ResolveAccent(document.RenderOptions.AccentColor);
var accent = TailoredCvTemplateCatalog.ResolveAccent(document.RenderOptions.AccentColor);
var body = RenderMainSections(document, accent, headingStyle: "harvard");
var contactLine = string.Join(" &nbsp;•&nbsp; ", new[]
{
@@ -135,7 +119,7 @@ public sealed class CvTemplateRenderer : ICvTemplateRenderer
private static string RenderSidebar(TailoredCvDocument document, string candidateName, string jobTitle, string? companyName, string? photoDataUrl, string templateLabel, bool roundedPhoto, bool curvedHeader)
{
var accent = ResolveAccent(document.RenderOptions.AccentColor);
var accent = TailoredCvTemplateCatalog.ResolveAccent(document.RenderOptions.AccentColor);
var showPhoto = document.RenderOptions.ShowPhoto && !string.IsNullOrWhiteSpace(photoDataUrl);
var sidebarSections = new StringBuilder();
sidebarSections.Append(RenderSidebarMetaSection("Personal Details", new[]
@@ -207,7 +191,7 @@ public sealed class CvTemplateRenderer : ICvTemplateRenderer
private static string RenderMonarch(TailoredCvDocument document, string candidateName, string jobTitle, string? companyName, string? photoDataUrl)
{
var accent = ResolveAccent(document.RenderOptions.AccentColor);
var accent = TailoredCvTemplateCatalog.ResolveAccent(document.RenderOptions.AccentColor);
var showPhoto = document.RenderOptions.ShowPhoto && !string.IsNullOrWhiteSpace(photoDataUrl);
var photoMarkup = showPhoto ? $"<div class=\"monarch-photo\"><img src=\"{EncodeAttribute(photoDataUrl)}\" alt=\"Profile photo\" /></div>" : string.Empty;
var body = RenderMainSections(document, accent, headingStyle: "sidebar");
@@ -259,7 +243,7 @@ public sealed class CvTemplateRenderer : ICvTemplateRenderer
private static string RenderFjord(TailoredCvDocument document, string candidateName, string jobTitle, string? companyName, string? photoDataUrl)
{
var accent = ResolveAccent(document.RenderOptions.AccentColor);
var accent = TailoredCvTemplateCatalog.ResolveAccent(document.RenderOptions.AccentColor);
var showPhoto = document.RenderOptions.ShowPhoto && !string.IsNullOrWhiteSpace(photoDataUrl);
var body = RenderMainSections(document, accent, headingStyle: "sidebar");
var photoMarkup = showPhoto ? $"<div class=\"fjord-photo\"><img src=\"{EncodeAttribute(photoDataUrl)}\" alt=\"Profile photo\" /></div>" : string.Empty;
@@ -445,21 +429,6 @@ public sealed class CvTemplateRenderer : ICvTemplateRenderer
return $"{normalizedStart} - {(isCurrent ? "Present" : string.IsNullOrWhiteSpace(normalizedEnd) ? "Present" : normalizedEnd)}";
}
private static string ResolveAccent(string? accentColor)
{
var normalized = (accentColor ?? string.Empty).Trim().ToLowerInvariant();
return normalized switch
{
"slate" => "#334155",
"blue" => "#1d4ed8",
"emerald" => "#047857",
"plum" => "#7c3aed",
"brick" => "#b45309",
_ when normalized.StartsWith("#") => normalized,
_ => "#334155"
};
}
private static string Encode(string? value) => WebUtility.HtmlEncode(value ?? string.Empty);
private static string EncodeAttribute(string? value) => WebUtility.HtmlEncode(value ?? string.Empty).Replace("'", "&#39;", StringComparison.Ordinal);