feat(cv): rebuild professional resume studio
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
using System.Globalization;
|
||||
using JobTrackerApi.Models;
|
||||
|
||||
namespace JobTrackerApi.Services;
|
||||
@@ -64,10 +65,17 @@ public static class CvVariantResolver
|
||||
};
|
||||
|
||||
AddContact(model.Contact, "email", profile.Contact.Email, v => $"mailto:{v}");
|
||||
AddContact(model.Contact, "phone", profile.Contact.Phone, null);
|
||||
AddContact(model.Contact, "phone", profile.Contact.Phone, v => $"tel:{new string(v.Where(character => char.IsDigit(character) || character == '+').ToArray())}");
|
||||
AddContact(model.Contact, "location", profile.Contact.Location, null);
|
||||
AddContact(model.Contact, "web", profile.Contact.Website, AsUrl);
|
||||
AddContact(model.Contact, "linkedin", profile.Contact.LinkedIn, AsUrl);
|
||||
AddContact(model.Contact, "web", profile.Contact.GitHub, AsUrl);
|
||||
foreach (var link in profile.Contact.Links)
|
||||
{
|
||||
var url = Trim(link.Url);
|
||||
if (url is null) continue;
|
||||
model.Contact.Add(new CvContactItem { Icon = "web", Value = Trim(link.Label) ?? url, Href = AsUrl(url) });
|
||||
}
|
||||
|
||||
var built = new Dictionary<string, CvRenderSection>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
@@ -84,6 +92,7 @@ public static class CvVariantResolver
|
||||
["organisations"] = BulletSection("organisations", "Organisations", profile.Organisations),
|
||||
["references"] = BulletSection("references", "References", profile.References),
|
||||
};
|
||||
ApplyLanguageLabels(built, settings.Language);
|
||||
|
||||
// OtherSections from the master profile become body sections keyed other:<n>.
|
||||
for (var i = 0; i < profile.OtherSections.Count; i++)
|
||||
@@ -160,7 +169,7 @@ public static class CvVariantResolver
|
||||
Key = job.Id,
|
||||
Title = Trim(ov?.Title) ?? Trim(job.Title),
|
||||
Subtitle = Trim(ov?.Subtitle) ?? JoinDot(job.Company, job.Location),
|
||||
Meta = DateRange(job.Start, job.End, job.IsCurrent),
|
||||
Meta = DateRange(job.StartDate ?? job.Start, job.EndDate ?? job.End, job.IsCurrent, settings),
|
||||
Bullets = ov?.Bullets != null ? Clean(ov.Bullets) : Clean(job.Bullets),
|
||||
Tags = Clean(job.Skills),
|
||||
});
|
||||
@@ -181,7 +190,7 @@ public static class CvVariantResolver
|
||||
Key = ed.Id,
|
||||
Title = Trim(ov?.Title) ?? title,
|
||||
Subtitle = Trim(ov?.Subtitle) ?? JoinDot(ed.Institution, ed.Location),
|
||||
Meta = DateRange(ed.Start, ed.End, false),
|
||||
Meta = DateRange(ed.StartDate ?? ed.Start, ed.EndDate ?? ed.End, false, settings),
|
||||
Bullets = ov?.Bullets != null ? Clean(ov.Bullets) : Clean(ed.Details),
|
||||
});
|
||||
}
|
||||
@@ -200,7 +209,7 @@ public static class CvVariantResolver
|
||||
Key = pr.Id,
|
||||
Title = Trim(ov?.Title) ?? Trim(pr.Name),
|
||||
Subtitle = Trim(ov?.Subtitle) ?? JoinDot(pr.Role, pr.Location),
|
||||
Meta = DateRange(pr.Start, pr.End, false),
|
||||
Meta = DateRange(pr.StartDate ?? pr.Start, pr.EndDate ?? pr.End, false, settings),
|
||||
Bullets = ov?.Bullets != null ? Clean(ov.Bullets) : Clean(pr.Bullets),
|
||||
Tags = Clean(pr.Skills),
|
||||
});
|
||||
@@ -279,13 +288,45 @@ public static class CvVariantResolver
|
||||
return string.IsNullOrWhiteSpace(joined) ? null : joined;
|
||||
}
|
||||
|
||||
private static string? DateRange(string? start, string? end, bool isCurrent)
|
||||
private static string? DateRange(string? start, string? end, bool isCurrent, CvVariantSettings settings)
|
||||
{
|
||||
var s = Trim(start);
|
||||
var e = Trim(end);
|
||||
var s = FormatDate(Trim(start), settings);
|
||||
var e = FormatDate(Trim(end), settings);
|
||||
if (s is null && e is null) return null;
|
||||
if (s is null) return e;
|
||||
return $"{s} – {(isCurrent ? "Present" : e ?? "Present")}";
|
||||
var present = IsNorwegian(settings.Language) ? "nå" : "Present";
|
||||
return $"{s} – {(isCurrent ? present : e ?? present)}";
|
||||
}
|
||||
|
||||
private static string? FormatDate(string? value, CvVariantSettings settings)
|
||||
{
|
||||
if (value is null || settings.DateFormat is null) return value;
|
||||
if (!DateTime.TryParseExact(value, new[] { "yyyy-MM", "yyyy-M", "yyyy-MM-dd", "yyyy" }, CultureInfo.InvariantCulture, DateTimeStyles.None, out var date)) return value;
|
||||
var culture = IsNorwegian(settings.Language) ? CultureInfo.GetCultureInfo("nb-NO") : CultureInfo.GetCultureInfo("en-GB");
|
||||
return settings.DateFormat switch
|
||||
{
|
||||
"year" => date.ToString("yyyy", culture),
|
||||
"numeric" => date.ToString("MM/yyyy", culture),
|
||||
"long" => date.ToString("MMMM yyyy", culture),
|
||||
_ => date.ToString("MMM yyyy", culture),
|
||||
};
|
||||
}
|
||||
|
||||
private static bool IsNorwegian(string? language) => language?.StartsWith("no", StringComparison.OrdinalIgnoreCase) == true
|
||||
|| language?.StartsWith("nb", StringComparison.OrdinalIgnoreCase) == true
|
||||
|| language?.StartsWith("nn", StringComparison.OrdinalIgnoreCase) == true;
|
||||
|
||||
private static void ApplyLanguageLabels(Dictionary<string, CvRenderSection> sections, string? language)
|
||||
{
|
||||
if (!IsNorwegian(language)) return;
|
||||
var labels = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
["summary"] = "Profil", ["experience"] = "Arbeidserfaring", ["education"] = "Utdanning",
|
||||
["projects"] = "Prosjekter", ["skills"] = "Ferdigheter", ["certifications"] = "Sertifiseringer",
|
||||
["languages"] = "Språk", ["interests"] = "Interesser", ["awards"] = "Priser og utmerkelser",
|
||||
["publications"] = "Publikasjoner", ["organisations"] = "Organisasjoner", ["references"] = "Referanser",
|
||||
};
|
||||
foreach (var (key, label) in labels) if (sections.TryGetValue(key, out var section)) section.Title = label;
|
||||
}
|
||||
|
||||
private static List<string> Clean(IEnumerable<string>? items) =>
|
||||
|
||||
Reference in New Issue
Block a user