Phase 1: project foundation for Alpenwerk HR

Scaffolds the Next.js 16 / TypeScript strict / Tailwind v3 app per
NEXTJS_REBUILD_SUPERPROMPT.md, and implements the Foundation slice from
the Phase 1 plan:

- Corrected Supabase schema (supabase/schema.sql): org units, employees,
  history, positions, hire drafts, saved reports, audit log, reorg
  scenarios, role-based profiles, salary-masking view, RLS policies,
  auto-derivation triggers, position-number generator.
- Seed script (supabase/seed.ts): ~800 realistic Austrian employees across
  9 divisions / 16 departments / 35 teams, history, 8 open positions, and
  hr_admin/manager test accounts.
- Supabase clients (lib/supabase/*), design tokens (tailwind.config.ts),
  format/color helpers (lib/format.ts, lib/colors.ts).
- Shared UI kit (components/ui): Avatar, StatusChip, Toast, Modal,
  SlideOver, SegmentedControl, Lookup.
- Auth (login page, Server Actions) and proxy.ts (Next 16's replacement
  for middleware) guarding the authenticated route group.
- Shell (Sidebar, Topbar, NewHireButton stub) and the Dashboard page,
  reading live data via employees_directory.

Employees list/detail, hire wizard, action panels, org chart, positions,
reports, and audit log are deferred to later phases per the plan.
This commit is contained in:
2026-07-13 21:44:28 +02:00
parent d3a5a9fa27
commit ef9852b09c
41 changed files with 9579 additions and 0 deletions

63
lib/colors.ts Normal file
View File

@@ -0,0 +1,63 @@
import type { EmploymentStatus } from "./supabase/types";
const AVATAR_PALETTE = [
"#d6046e",
"#00666d",
"#5c2e91",
"#835b00",
"#0e700e",
"#b0035a",
"#2b6cb0",
"#a30354",
];
// Stable per-employee avatar color when employees.avatar_color isn't set.
export function avatarColorFor(seed: string): string {
let hash = 0;
for (let i = 0; i < seed.length; i++) {
hash = (hash << 5) - hash + seed.charCodeAt(i);
hash |= 0;
}
return AVATAR_PALETTE[Math.abs(hash) % AVATAR_PALETTE.length];
}
export const STATUS_STYLES: Record<EmploymentStatus, string> = {
Aktiv: "bg-success-bg text-success-text",
Karenz: "bg-warning-bg text-warning-text",
Geplant: "bg-brand-100 text-brand-700",
Ausgetreten: "bg-danger-bg text-danger-text",
};
type ColorCategory = "success" | "danger" | "warning" | "info" | "purple" | "brand";
const CATEGORY_STYLES: Record<ColorCategory, string> = {
success: "bg-success-bg text-success-text",
danger: "bg-danger-bg text-danger-text",
warning: "bg-warning-bg text-warning-text",
info: "bg-info-bg text-info-text",
purple: "bg-purple-bg text-purple-text",
brand: "bg-brand-100 text-brand-700",
};
// Audit-log / activity-feed action -> badge color, per the action list in
// supabase/schema.sql's audit_log comment.
const ACTION_CATEGORY: Record<string, ColorCategory> = {
Neueinstellung: "success",
Wiedereinstellung: "success",
Rückkehr: "success",
Austritt: "danger",
Versetzung: "info",
Ausschreibung: "info",
"Interne Besetzung": "info",
Beförderung: "purple",
Reorganisation: "purple",
"Reorganisation rückgängig": "purple",
Karenz: "warning",
Vertragsänderung: "warning",
Stammdatenänderung: "warning",
Gehaltsanpassung: "warning",
};
export function actionBadgeStyle(action: string): string {
return CATEGORY_STYLES[ACTION_CATEGORY[action] ?? "brand"];
}