Reworks the app from a two-role (hr_admin/manager) model to a single HR-only role gated by profiles.is_active, fixes transfer/promote/karenz/ reorg RPCs to actually defer future-dated changes via a new pending_org_changes table instead of writing them immediately (applied by a daily Vercel Cron route), makes reorg undo append-only instead of deleting history, adds Karenz-return and history-date integrity guards, deprecates the salary column, and adds explicit schema grants + perf indexes needed to run against a fresh (non-hosted) Postgres instance. Adds vitest unit + integration test suites (the latter against a real local Supabase instance) covering all of the above, plus lint/typecheck/ build wiring (`npm run check`).
55 lines
2.0 KiB
TypeScript
55 lines
2.0 KiB
TypeScript
const dateFormatter = new Intl.DateTimeFormat("de-AT", {
|
||
day: "2-digit",
|
||
month: "2-digit",
|
||
year: "numeric",
|
||
});
|
||
|
||
export function fmtDate(date: string | Date | null | undefined): string {
|
||
if (!date) return "–";
|
||
const d = typeof date === "string" ? new Date(date) : date;
|
||
if (Number.isNaN(d.getTime())) return "–";
|
||
return dateFormatter.format(d);
|
||
}
|
||
|
||
export function initials(firstName: string, lastName: string): string {
|
||
const a = firstName.trim().charAt(0).toUpperCase();
|
||
const b = lastName.trim().charAt(0).toUpperCase();
|
||
return `${a}${b}`;
|
||
}
|
||
|
||
export function fmtAge(birthDate: string | Date): number {
|
||
const d = typeof birthDate === "string" ? new Date(birthDate) : birthDate;
|
||
const today = new Date();
|
||
let age = today.getFullYear() - d.getFullYear();
|
||
const hasHadBirthdayThisYear =
|
||
today.getMonth() > d.getMonth() ||
|
||
(today.getMonth() === d.getMonth() && today.getDate() >= d.getDate());
|
||
if (!hasHadBirthdayThisYear) age -= 1;
|
||
return age;
|
||
}
|
||
|
||
export function tenure(entryDate: string | Date, endDate?: string | Date | null): string {
|
||
const start = typeof entryDate === "string" ? new Date(entryDate) : entryDate;
|
||
const end = endDate ? (typeof endDate === "string" ? new Date(endDate) : endDate) : new Date();
|
||
|
||
let years = end.getFullYear() - start.getFullYear();
|
||
let months = end.getMonth() - start.getMonth();
|
||
if (end.getDate() < start.getDate()) months -= 1;
|
||
if (months < 0) {
|
||
years -= 1;
|
||
months += 12;
|
||
}
|
||
if (years < 0) return "0 Monate";
|
||
|
||
const yearPart = years > 0 ? `${years} ${years === 1 ? "Jahr" : "Jahre"}` : "";
|
||
const monthPart = months > 0 ? `${months} ${months === 1 ? "Monat" : "Monate"}` : "";
|
||
if (yearPart && monthPart) return `${yearPart}, ${monthPart}`;
|
||
return yearPart || monthPart || "unter 1 Monat";
|
||
}
|
||
|
||
export function daysBetween(a: string | Date, b: string | Date = new Date()): number {
|
||
const start = typeof a === "string" ? new Date(a) : a;
|
||
const end = typeof b === "string" ? new Date(b) : b;
|
||
return Math.round((end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24));
|
||
}
|