"use client"; import Link from "next/link"; import { useRef, useState, useTransition } from "react"; import type { Project, ProjectExpertScore, RikxRonde } from "../../../lib/db"; import { PROJECT_STATUSES } from "../../../lib/projectStatus"; import { BONUS_MALUS_OPTIONS, EXPERT_COUNT, EXPERT_CRITERIA, calculateRikx, type ExpertScore, } from "../../../lib/rikxCalculatie"; import { parseProjectPlanJson } from "../../../lib/projectPlan"; import { setRikxCalculatie, updateProject } from "../actions"; import { ProjectPlanFields } from "./ProjectPlanFields"; const inputClass = "w-full h-10 rounded-lg border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-800 px-3 text-sm text-zinc-900 dark:text-zinc-100 focus:outline-none focus:ring-2 focus:ring-rikx-orange"; const labelClass = "text-sm font-medium text-zinc-700 dark:text-zinc-300"; const cardClass = "bg-white dark:bg-zinc-900 rounded-xl border border-zinc-200 dark:border-zinc-800 p-6"; function toNumber(value: FormDataEntryValue | null): number | null { if (typeof value !== "string" || value.trim() === "") return null; const n = Number(value.replace(",", ".")); return Number.isFinite(n) ? n : null; } function rondeById(rondes: RikxRonde[], id: number | null) { return rondes.find((r) => r.id === id) ?? null; } function readInput(form: HTMLFormElement, rondes: RikxRonde[]) { const fd = new FormData(form); const ronde = rondeById(rondes, Number(fd.get("ronde_id")) || null); return { ronde_id: ronde?.id ?? null, projectkosten: toNumber(fd.get("projectkosten")), deelnemers_werk: toNumber(fd.get("deelnemers_werk")), deelnemers_skills: toNumber(fd.get("deelnemers_skills")), financiering_extern_pct: toNumber(fd.get("financiering_extern_pct")), bonus_malus: (fd.get("bonus_malus") as string) || null, rikx_waarde: ronde?.rikx_waarde ?? null, rikx_prijs: ronde?.rikx_prijs ?? null, expert_scores: Array.from({ length: EXPERT_COUNT }, (_, i) => Object.fromEntries(EXPERT_CRITERIA.map((c) => [c.id, toNumber(fd.get(`expert_${i + 1}_${c.id}`))])) ) as ExpertScore[], }; } const nl = (digits: number) => new Intl.NumberFormat("nl-NL", { maximumFractionDigits: digits }); const euro = new Intl.NumberFormat("nl-NL", { style: "currency", currency: "EUR" }); const fmt = (v: number | null, kind: "getal" | "euro" | "geheel" = "getal") => v === null ? "—" : kind === "euro" ? euro.format(v) : nl(kind === "geheel" ? 0 : 2).format(v); function Field({ label, hint, children, }: { label: string; hint?: string; children: React.ReactNode; }) { return ( ); } function StepHeading({ step, title, description }: { step: number; title: string; description: string }) { return (
{step}

{title}

{description}

); } export function ProjectDetailForm({ project, expertScores, rondes, }: { project: Project; expertScores: ProjectExpertScore[]; rondes: RikxRonde[]; }) { const formRef = useRef(null); const [calcOn, setCalcOn] = useState(project.rikx_calculatie_aan === 1); const [pending, startTransition] = useTransition(); const initialRonde = rondeById(rondes, project.ronde_id); const [input, setInput] = useState(() => ({ ronde_id: initialRonde?.id ?? null, projectkosten: project.projectkosten, deelnemers_werk: project.deelnemers_werk, deelnemers_skills: project.deelnemers_skills, financiering_extern_pct: project.financiering_extern_pct, bonus_malus: project.bonus_malus, rikx_waarde: initialRonde?.rikx_waarde ?? null, rikx_prijs: initialRonde?.rikx_prijs ?? null, expert_scores: Array.from({ length: EXPERT_COUNT }, (_, i) => { const s = expertScores.find((e) => e.expert === i + 1); return { duur_impact: s?.duur_impact ?? null, levensdomeinen: s?.levensdomeinen ?? null, basissituatie: s?.basissituatie ?? null, }; }), })); const result = calculateRikx(input); const ronde = rondeById(rondes, input.ronde_id); function toggleCalc() { const next = !calcOn; setCalcOn(next); startTransition(() => setRikxCalculatie(project.id, next)); } const results: { label: string; value: string; strong?: boolean }[] = [ { label: "N-score", value: fmt(result.n_score) }, { label: "Scale", value: fmt(result.scale, "geheel") }, { label: "Impactscore M", value: fmt(result.impactscore_m) }, { label: "IS +/- BM", value: fmt(result.is_bm) }, { label: "Impactwaarde", value: fmt(result.impactwaarde) }, { label: "Minus cofinanciering", value: fmt(result.minus_cofinanciering) }, { label: "I - cofin", value: fmt(result.i_cofin) }, { label: "Kosten/impact", value: fmt(result.kosten_impact) }, { label: "Totale impactwaarde", value: fmt(result.totale_impactwaarde, "euro"), strong: true }, { label: "Totaal aantal Rikx", value: fmt(result.totaal_aantal_rikx), strong: true }, { label: "Meerwaarde", value: fmt(result.meerwaarde, "euro"), strong: true }, { label: "Efficiëntiewaarde", value: fmt(result.efficientie_waarde), strong: true }, ]; return (
formRef.current && setInput(readInput(formRef.current, rondes))} className="flex flex-col gap-6" >

Rikx calculatie

Toon de waardebepaling volgens de Rikx-rekenmethode.

{/* Verborgen in plaats van weggelaten, zodat de waarden bij opslaan bewaard blijven. */}
Annuleren
); }