Files
jobtrackingapp/JobTrackerApi/Controllers/CompaniesController.cs
2026-03-21 14:02:19 +01:00

151 lines
5.9 KiB
C#

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using JobTrackerApi.Data;
using JobTrackerApi.Models;
using System.Security.Claims;
namespace JobTrackerApi.Controllers
{
[ApiController]
[Route("api/companies")]
public class CompaniesController : ControllerBase
{
private readonly JobTrackerContext _db;
public CompaniesController(JobTrackerContext db)
{
_db = db;
}
private string? CurrentUserId =>
User?.FindFirstValue(ClaimTypes.NameIdentifier) ?? User?.FindFirstValue("sub");
private static string? NormalizeSource(string? source)
{
if (string.IsNullOrWhiteSpace(source)) return null;
var value = source.Trim();
if (Uri.TryCreate(value, UriKind.Absolute, out var uri) && !string.IsNullOrWhiteSpace(uri.Host))
{
value = uri.Host;
}
value = value.Replace("www.", "", StringComparison.OrdinalIgnoreCase).Trim().Trim('/');
var lower = value.ToLowerInvariant();
return lower switch
{
"linkedin" or "linkedin.com" => "LinkedIn",
"finn" or "finn.no" => "Finn",
"nav" or "nav.no" => "NAV",
"jobbnorge" or "jobbnorge.no" => "Jobbnorge",
_ => string.Join(" ", value.Split(new[] { ' ', '-', '_' }, StringSplitOptions.RemoveEmptyEntries).Select(part => char.ToUpperInvariant(part[0]) + part[1..].ToLowerInvariant()))
};
}
[HttpGet]
public async Task<ActionResult<List<Company>>> GetAll(CancellationToken cancellationToken)
{
var userId = CurrentUserId;
var q = _db.Companies.AsQueryable();
if (!string.IsNullOrWhiteSpace(userId))
q = q.Where(c => c.OwnerUserId == userId);
var companies = await q
.OrderBy(c => c.Name)
.ToListAsync(cancellationToken);
return Ok(companies);
}
[HttpGet("{id:int}")]
public async Task<ActionResult<Company>> GetById([FromRoute] int id, CancellationToken cancellationToken)
{
var userId = CurrentUserId;
var q = _db.Companies.AsQueryable();
if (!string.IsNullOrWhiteSpace(userId))
q = q.Where(c => c.OwnerUserId == userId);
var company = await q.FirstOrDefaultAsync(c => c.Id == id, cancellationToken);
if (company is null) return NotFound();
return Ok(company);
}
public sealed record CreateCompanyRequest(string Name, string? Location, string? Source);
public sealed record UpdateCompanyRequest(
string Name,
string? Location,
string? Source,
string? RecruiterName,
string? RecruiterEmail,
string? RecruiterLinkedIn,
DateTime? LastContactedAt,
DateTime? NextContactAt,
string? PipelineStage
);
[HttpPost]
public async Task<ActionResult<Company>> Create([FromBody] CreateCompanyRequest request, CancellationToken cancellationToken)
{
var userId = CurrentUserId;
var name = (request.Name ?? "").Trim();
if (name.Length == 0) return BadRequest("Company name is required.");
var existingQuery = _db.Companies.AsQueryable();
if (!string.IsNullOrWhiteSpace(userId))
existingQuery = existingQuery.Where(c => c.OwnerUserId == userId);
var existing = await existingQuery
.FirstOrDefaultAsync(c => c.Name.ToLower() == name.ToLower(), cancellationToken);
if (existing is not null)
{
// Idempotent create: return existing instead of failing.
return Ok(existing);
}
var company = new Company
{
OwnerUserId = string.IsNullOrWhiteSpace(userId) ? null : userId,
Name = name,
Location = string.IsNullOrWhiteSpace(request.Location) ? null : request.Location.Trim(),
Source = NormalizeSource(request.Source),
};
_db.Companies.Add(company);
await _db.SaveChangesAsync(cancellationToken);
return CreatedAtAction(nameof(GetById), new { id = company.Id }, company);
}
[HttpPut("{id:int}")]
public async Task<ActionResult<Company>> Update([FromRoute] int id, [FromBody] UpdateCompanyRequest request, CancellationToken cancellationToken)
{
var userId = CurrentUserId;
var q = _db.Companies.AsQueryable();
if (!string.IsNullOrWhiteSpace(userId))
q = q.Where(c => c.OwnerUserId == userId);
var company = await q.FirstOrDefaultAsync(c => c.Id == id, cancellationToken);
if (company is null) return NotFound();
var name = (request.Name ?? "").Trim();
if (name.Length == 0) return BadRequest("Company name is required.");
company.Name = name;
company.Location = string.IsNullOrWhiteSpace(request.Location) ? null : request.Location.Trim();
company.Source = NormalizeSource(request.Source);
company.RecruiterName = string.IsNullOrWhiteSpace(request.RecruiterName) ? null : request.RecruiterName.Trim();
company.RecruiterEmail = string.IsNullOrWhiteSpace(request.RecruiterEmail) ? null : request.RecruiterEmail.Trim();
company.RecruiterLinkedIn = string.IsNullOrWhiteSpace(request.RecruiterLinkedIn) ? null : request.RecruiterLinkedIn.Trim();
company.PipelineStage = string.IsNullOrWhiteSpace(request.PipelineStage) ? null : request.PipelineStage.Trim();
company.LastContactedAt = request.LastContactedAt;
company.NextContactAt = request.NextContactAt;
await _db.SaveChangesAsync(cancellationToken);
return Ok(company);
}
}
}