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

View File

@@ -0,0 +1,48 @@
"use client";
import { LogOut } from "lucide-react";
import { usePathname } from "next/navigation";
import { logout } from "@/actions/auth";
import { NewHireButton } from "./NewHireButton";
const TITLES: Record<string, string> = {
"/": "Übersicht",
"/employees": "Mitarbeiter:innen",
"/orgchart": "Organigramm",
"/positions": "Positionen & Bereiche",
"/reports": "Berichte",
"/audit": "Audit-Log",
};
function titleFor(pathname: string): string {
if (TITLES[pathname]) return TITLES[pathname];
const match = Object.keys(TITLES).find((key) => key !== "/" && pathname.startsWith(key));
return match ? TITLES[match] : "Alpenwerk HR";
}
type TopbarProps = {
userLabel: string;
role?: string;
};
export function Topbar({ userLabel, role }: TopbarProps) {
const pathname = usePathname();
return (
<header className="flex h-14 items-center justify-between border-b border-border bg-white px-6">
<h1 className="text-base font-bold text-ink">{titleFor(pathname)}</h1>
<div className="flex items-center gap-4">
<NewHireButton />
<div className="flex items-center gap-2 border-l border-border pl-4 text-sm">
<span className="font-semibold text-ink">{userLabel}</span>
{role && <span className="text-xs text-ink-muted">({role === "hr_admin" ? "HR-Admin" : "Manager"})</span>}
<form action={logout}>
<button type="submit" aria-label="Abmelden" className="ml-2 rounded p-1.5 text-ink-muted hover:bg-surface">
<LogOut className="h-4 w-4" />
</button>
</form>
</div>
</div>
</header>
);
}