Track cleanup progress and polish profile/system flows

This commit is contained in:
cesnimda
2026-03-23 19:49:41 +01:00
parent fcafda6f52
commit 90fdd8e1a5
14 changed files with 156 additions and 49 deletions
+3 -3
View File
@@ -133,7 +133,7 @@ public sealed class AuthController : ControllerBase
if (user is null)
{
return Unauthorized("This Google account is not linked to a Job Tracker user yet.");
return Unauthorized("This Google account is not linked to a Jobbjakt user yet.");
}
if (string.IsNullOrWhiteSpace(user.GoogleSubject) || !string.Equals(user.GoogleSubject, google.Subject, StringComparison.Ordinal))
@@ -237,7 +237,7 @@ public sealed class AuthController : ControllerBase
.FirstOrDefaultAsync(x => x.GoogleSubject == google.Subject || (!string.IsNullOrWhiteSpace(google.Email) && x.GoogleEmail == google.Email), cancellationToken);
if (conflict is not null)
{
return Conflict("That Google account is already linked to another Job Tracker user.");
return Conflict("That Google account is already linked to another Jobbjakt user.");
}
user.GoogleSubject = google.Subject;
@@ -393,7 +393,7 @@ public sealed class AuthController : ControllerBase
await _email.SendAsync(
user.Email,
"Password reset",
$"You requested a password reset for Job Tracker.\n\nReset link:\n{link}\n\nIf you did not request this, you can ignore this email.",
$"You requested a password reset for Jobbjakt.\n\nReset link:\n{link}\n\nIf you did not request this, you can ignore this email.",
cancellationToken
);
@@ -6,6 +6,7 @@ using JobTrackerApi.Services;
using JobTrackerApi.Services.JobImport;
using System.Security.Claims;
using System.Text.Json;
using Microsoft.AspNetCore.Identity;
namespace JobTrackerApi.Controllers
{
@@ -16,17 +17,44 @@ namespace JobTrackerApi.Controllers
private readonly JobTrackerContext _db;
private readonly ISummarizerService _summarizer;
private readonly IAppEmailSender _email;
private readonly UserManager<ApplicationUser> _users;
public JobApplicationsController(JobTrackerContext db, ISummarizerService summarizer, IAppEmailSender email)
public JobApplicationsController(JobTrackerContext db, ISummarizerService summarizer, IAppEmailSender email, UserManager<ApplicationUser> users)
{
_db = db;
_summarizer = summarizer;
_email = email;
_users = users;
}
private string? CurrentUserId =>
User?.FindFirstValue(ClaimTypes.NameIdentifier) ?? User?.FindFirstValue("sub");
private async Task<ApplicationUser?> GetCurrentUserAsync(CancellationToken cancellationToken)
{
var userId = CurrentUserId;
if (string.IsNullOrWhiteSpace(userId)) return null;
return await _users.FindByIdAsync(userId);
}
private static string GetPreferredDisplayName(ApplicationUser? user)
{
if (user is null) return "Your Name";
if (!string.IsNullOrWhiteSpace(user.DisplayName)) return user.DisplayName.Trim();
var fullName = string.Join(" ", new[] { user.FirstName?.Trim(), user.LastName?.Trim() }.Where(x => !string.IsNullOrWhiteSpace(x)));
if (!string.IsNullOrWhiteSpace(fullName)) return fullName;
if (!string.IsNullOrWhiteSpace(user.UserName)) return user.UserName.Trim();
if (!string.IsNullOrWhiteSpace(user.Email)) return user.Email.Trim();
return "Your Name";
}
private static string BuildGreeting(JobApplication job)
{
if (!string.IsNullOrWhiteSpace(job.Company?.RecruiterName)) return $"Hi {job.Company.RecruiterName.Trim()},";
if (!string.IsNullOrWhiteSpace(job.Company?.Name)) return $"Hi {job.Company.Name.Trim()} team,";
return "Hi there,";
}
private static IEnumerable<string> SplitTags(string? s)
{
if (string.IsNullOrWhiteSpace(s)) yield break;
@@ -1747,18 +1775,20 @@ Candidate master CV:
? (job.FollowUpAt is not null && job.FollowUpAt.Value.Date <= DateTime.Today ? "Scheduled follow-up is due." : "No recent response has been logged.")
: job.NextAction!;
var currentUser = await GetCurrentUserAsync(cancellationToken);
var signerName = GetPreferredDisplayName(currentUser);
var greeting = BuildGreeting(job);
var subject = $"Following up on {job.JobTitle} application";
var companyName = job.Company?.Name ?? "your team";
var reference = lastMessage?.Subject ?? job.JobTitle;
var summary = job.ShortSummary;
var body = string.Join("\n\n", new[]
{
$"Hi {companyName},",
greeting,
$"I wanted to follow up on my application for the {job.JobTitle} role. I'm still very interested in the opportunity and would love to hear if there are any updates on next steps.",
!string.IsNullOrWhiteSpace(summary) ? $"Quick reminder of fit: {summary}" : null,
$"Context: {reason}",
$"If helpful, I can also provide any additional information related to {reference}.",
"Thanks for your time,\n[Your name]"
$"Thanks for your time,\n{signerName}"
}.Where(x => !string.IsNullOrWhiteSpace(x)));
return Ok(new FollowUpDraftDto(subject, body, reason, DateTime.Today));
@@ -20,7 +20,7 @@ public sealed class ProfileCvController : ControllerBase
".docx",
};
private const long MaxFileSizeBytes = 512 * 1024;
private const long MaxFileSizeBytes = 5 * 1024 * 1024;
private readonly UserManager<ApplicationUser> _users;
@@ -36,7 +36,7 @@ public sealed class ProfileCvController : ControllerBase
var user = await _users.GetUserAsync(User);
if (user is null) return Unauthorized();
if (file is null || file.Length == 0) return BadRequest("Select a CV file to upload.");
if (file.Length > MaxFileSizeBytes) return BadRequest("CV import file is too large.");
if (file.Length > MaxFileSizeBytes) return BadRequest("CV import file is too large. Keep it under 5 MB.");
var extension = Path.GetExtension(file.FileName ?? string.Empty);
if (!AllowedExtensions.Contains(extension))
@@ -78,7 +78,10 @@ public sealed class ProfileCvController : ControllerBase
var raw = Encoding.UTF8.GetString(bytes);
var textMatches = Regex.Matches(raw, @"\((.*?)\)Tj", RegexOptions.Singleline)
.Select(match => match.Groups[1].Value)
.Concat(Regex.Matches(raw, @"\[(.*?)\]TJ", RegexOptions.Singleline)
.SelectMany(match => Regex.Matches(match.Groups[1].Value, @"\((.*?)\)", RegexOptions.Singleline).Select(x => x.Groups[1].Value)))
.Where(value => !string.IsNullOrWhiteSpace(value))
.Select(value => Regex.Unescape(value))
.ToList();
var joined = textMatches.Count > 0 ? string.Join(" ", textMatches) : raw;
+3 -3
View File
@@ -153,7 +153,7 @@ public sealed class UsersController : ControllerBase
await _email.SendAsync(
u.Email,
"Password reset",
$"An admin initiated a password reset for your Job Tracker account.\n\nReset link:\n{link}\n",
$"An admin initiated a password reset for your Jobbjakt account.\n\nReset link:\n{link}\n",
cancellationToken
);
@@ -171,9 +171,9 @@ public sealed class UsersController : ControllerBase
var toEmail = (request?.ToEmail ?? currentUser?.Email ?? "").Trim();
if (string.IsNullOrWhiteSpace(toEmail)) return BadRequest("Recipient email is required.");
var subject = string.IsNullOrWhiteSpace(request?.Subject) ? "Job Tracker test email" : request!.Subject!.Trim();
var subject = string.IsNullOrWhiteSpace(request?.Subject) ? "Jobbjakt test email" : request!.Subject!.Trim();
var message = string.IsNullOrWhiteSpace(request?.Message)
? "This is a test email from the Job Tracker admin panel.\n\nIf you received this, the SMTP configuration is working."
? "This is a test email from the Jobbjakt admin panel.\n\nIf you received this, the SMTP configuration is working."
: request!.Message!.Trim();
await _email.SendAsync(