Voeg Module 1 Projecten toe met project plan, Rikx calculatie en Rikx rondes

- Projectenpagina met pipeline (standaard) en tabelweergave, filters op
  status, Rikx Ronde en zoeken
- Projectpagina in stappen: Project plan (velden uit de Rikx-aanvraag) en
  Rikx calculatie achter een schuif, live berekend volgens de rekenmethode
- Rikx rondes beheren (label, Rikx waarde, Rikx prijs) en koppelen aan projecten
- Oranje Rikx-menubalk met menu per module, gedeeld door alle pagina's
- Oude offerte-tabellen opgeruimd: leeg wordt verwijderd, met data hernoemd
  naar legacy_*
- CLAUDE.md bijgewerkt naar de huidige stand

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-08 10:26:30 +02:00
parent 46eecc31a4
commit 9f0bd87c40
25 changed files with 2161 additions and 55 deletions
+129
View File
@@ -0,0 +1,129 @@
import Link from "next/link";
import { logout } from "./tenants/logout";
type MenuItem = { label: string; href?: string };
type NavItem = { label: string; section: string; href?: string; items?: MenuItem[] };
// Menu volgens de Rikx-modules. Items zonder href bestaan nog niet en tonen "binnenkort".
const NAV_ITEMS: NavItem[] = [
{ label: "Dashboard", section: "dashboard" },
{
label: "Projecten",
section: "projecten",
items: [
{ label: "Overzicht", href: "/projecten" },
{ label: "Project toevoegen", href: "/projecten/nieuw" },
{ label: "Rikx rondes", href: "/rondes" },
],
},
{
label: "Investeringen",
section: "investeringen",
items: [{ label: "Overzicht" }],
},
{
label: "CRM",
section: "crm",
items: [{ label: "Sociaal ondernemers", href: "/tenants" }, { label: "Investeerders" }, { label: "Beoordelaars" }],
},
{ label: "Publieke Website", section: "website" },
];
function Caret() {
return (
<svg viewBox="0 0 10 6" aria-hidden className="w-2.5 h-2.5 fill-current opacity-80">
<path d="M0 0h10L5 6z" />
</svg>
);
}
function linkClass(isActive: boolean) {
return isActive
? "text-rikx-purple"
: "text-white hover:text-rikx-maroon transition-colors";
}
export function AppHeader({ email, active }: { email: string; active?: string }) {
return (
<header className="bg-rikx-orange">
<div className="max-w-[96rem] mx-auto px-4 sm:px-6 py-3 flex flex-wrap items-center gap-x-8 gap-y-2">
<Link href="/projecten" className="text-white text-4xl font-extrabold tracking-tight leading-none">
Rikx
</Link>
<nav className="order-3 lg:order-none w-full lg:w-auto lg:flex-1 flex flex-wrap items-center lg:justify-center gap-x-6 gap-y-1 text-base sm:text-lg font-semibold">
{NAV_ITEMS.map((item) => {
const isActive = active === item.section;
if (!item.items) {
return item.href ? (
<Link key={item.label} href={item.href} className={linkClass(isActive)}>
{item.label}
</Link>
) : (
<span key={item.label} title="Binnenkort" className="text-white/60 cursor-default">
{item.label}
</span>
);
}
return (
<div key={item.label} className="relative group">
<button
type="button"
className={`flex items-center gap-1.5 py-1 ${linkClass(isActive)}`}
aria-haspopup="true"
>
{item.label}
<Caret />
</button>
<div className="absolute left-0 top-full z-20 hidden group-hover:block group-focus-within:block pt-1">
<ul className="min-w-52 rounded-lg bg-white dark:bg-zinc-900 shadow-lg ring-1 ring-black/5 py-1 text-sm font-medium">
{item.items.map((sub) => (
<li key={sub.label}>
{sub.href ? (
<Link
href={sub.href}
className="block px-4 py-2 text-zinc-800 dark:text-zinc-200 hover:bg-orange-50 dark:hover:bg-zinc-800 hover:text-rikx-orange-dark"
>
{sub.label}
</Link>
) : (
<span className="flex items-center justify-between gap-3 px-4 py-2 text-zinc-400 cursor-default">
{sub.label}
<span className="text-xs font-normal">binnenkort</span>
</span>
)}
</li>
))}
</ul>
</div>
</div>
);
})}
</nav>
<div className="relative group ml-auto">
<button
type="button"
aria-label="Account"
aria-haspopup="true"
className="flex items-center justify-center w-10 h-10 rounded-full bg-white/90 text-zinc-400 hover:bg-white"
>
<svg viewBox="0 0 24 24" aria-hidden className="w-7 h-7 fill-current">
<path d="M12 12a4.5 4.5 0 1 0 0-9 4.5 4.5 0 0 0 0 9Zm0 2c-4.4 0-8 2.2-8 5v1h16v-1c0-2.8-3.6-5-8-5Z" />
</svg>
</button>
<div className="absolute right-0 top-full z-20 hidden group-hover:block group-focus-within:block pt-1">
<div className="min-w-56 rounded-lg bg-white dark:bg-zinc-900 shadow-lg ring-1 ring-black/5 py-1 text-sm">
<p className="px-4 py-2 text-zinc-500 truncate">{email}</p>
<form action={logout}>
<button className="w-full text-left px-4 py-2 text-zinc-800 dark:text-zinc-200 hover:bg-orange-50 dark:hover:bg-zinc-800">
Uitloggen
</button>
</form>
</div>
</div>
</div>
</div>
</header>
);
}