83e6430a24
Adds SalaryMin/SalaryMax/SalaryCurrency/SalaryPeriod alongside the existing free-text Salary field (kept for back-compat and display). - JobApplication model + idempotent column bridging for SQLite and MySQL - Create/Update DTOs with NormalizeSalary (clamps negatives, swaps inverted min/max, uppercases currency, whitelists period) - JobApplicationDto exposes the fields; CSV export gains 4 columns - UI: add/edit dialogs get min/max/currency/period inputs; job table renders a formatted range via shared salary.ts formatter (falls back to free-text when structured values are absent) - EN/NB translations; backend + full frontend suites green Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
22 lines
998 B
TypeScript
22 lines
998 B
TypeScript
import { JobApplication } from "./types";
|
||
|
||
type SalaryFields = Pick<JobApplication, "salary" | "salaryMin" | "salaryMax" | "salaryCurrency" | "salaryPeriod">;
|
||
|
||
const PERIOD_SUFFIX: Record<string, string> = { year: "yr", month: "mo", hour: "hr" };
|
||
|
||
/** Structured salary when present ("60 000–70 000 NOK/yr"), otherwise the free-text field. */
|
||
export function formatSalary(job: SalaryFields): string | null {
|
||
const { salaryMin, salaryMax, salaryCurrency, salaryPeriod } = job;
|
||
if (salaryMin == null && salaryMax == null) {
|
||
return job.salary?.trim() || null;
|
||
}
|
||
|
||
const fmt = (value: number) => value.toLocaleString();
|
||
const range = salaryMin != null && salaryMax != null && salaryMin !== salaryMax
|
||
? `${fmt(salaryMin)}–${fmt(salaryMax)}`
|
||
: fmt((salaryMin ?? salaryMax) as number);
|
||
const currency = salaryCurrency ? ` ${salaryCurrency}` : "";
|
||
const period = salaryPeriod ? `/${PERIOD_SUFFIX[salaryPeriod] ?? salaryPeriod}` : "";
|
||
return `${range}${currency}${period}`;
|
||
}
|