// Nieuw scherm: een dag inrichten (starttijden genereren, sport per slot).
// Eigenaar: bouwer FLOW. Dit bestand was leeg bij de start van de balieronde.
//
// WAAROM DIT SCHERM ER IS. De database kan een dag inrichten met `zet_dagindeling` en `genereer_dag`,
// maar geen enkel scherm gebruikte die twee. Gevolg: als een dag de verkeerde sport had, moest dat
// door ons of door de bouwer bij UP in de database gebeuren. Precies daar liep de vraag op vast of
// vrijdagmiddag voetgolf is. Met dit scherm is dat een handeling aan de balie.
//
// HOE HET ONDER WATER WERKT, gemeten op de functiebron en niet aangenomen:
//   1. Een dag wijst naar een DAGSJABLOON (tabel `dagindeling`).
//   2. Een sjabloon bestaat uit BLOKKEN (`sjabloonblokken`), en elk blok draagt zijn eigen `sport`,
//      begintijd, eindtijd, interval en maximum aantal spelers.
//   3. `genereer_dag` loopt die blokken af en zet per blok starttijden neer MET de sport van dat blok.
//   4. Die insert doet `on conflict (baan_id, datum, starttijd) do nothing`. Bestaande starttijden
//      worden dus NIET bijgewerkt. Zonder opschonen verandert de sport van bestaande tijden niet.
//   5. Opschonen weigert als er nog actieve boekingen op die dag staan, en verwijdert alleen
//      starttijden met status `open`.
//   6. Beide functies eisen `is_beheerder()`, niet alleen medewerker.
// Die punten staan hieronder ook op het scherm zelf, want een balie hoort niet te hoeven raden
// waarom er niets veranderde.

// Het slot. Eén keer per sessie vragen, niet per scherm: anders is het onwerkbaar en hangt het binnen
// een week op een briefje naast de kassa.
//
// Er zitten twee soorten schermen achter: het inrichtingsdeel (tarieven, spelerstypes, dagindeling) en
// de rapportage (de omzet). Dat zijn twee verschillende redenen, dus het slotscherm zegt ook twee
// verschillende dingen. Wie het wachtwoord voor het ene deel heeft ingevoerd, komt ook in het andere;
// het is één wachtwoord en één sessie.
const SLOT_TEKST = {
  rapportage: {
    kop: "Wachtwoord voor de rapportage",
    waarom: "Hierachter staat de omzet, per periode en per dag. Dat hoeft niet de hele dag op het scherm te staan waar iedereen achter de balie langsloopt, dus er zit een wachtwoord op.",
    open: "Iedereen die is ingelogd kan de omzetcijfers en de inrichting bekijken en aanpassen."
  },
  standaard: {
    kop: "Wachtwoord voor de inrichting",
    waarom: "Hierachter staan de tarieven, de spelerstypes en de dagindeling. Een verkeerde wijziging is meteen zichtbaar voor gasten, dus er zit een wachtwoord op.",
    open: "Iedereen die is ingelogd kan hier de tarieven, de spelerstypes en de dagen aanpassen."
  }
};
// ===== Waarom het wachtwoord niet meer in de browser wordt gecontroleerd ================
// Het oude slot rekende in de browser: het haalde de opgeslagen waarde op uit `instellingen` en
// vergeleek zelf. `instellingen` heeft een leespolicy USING (true) voor de ANONIEME rol, dus die
// waarde kon iedereen met het publieke sleuteltje downloaden en er thuis rustig op zitten raden.
// Nu staat de hash in een eigen tabel zonder leesrecht en gebeurt de vergelijking in de database,
// in `open_sleutel`, die alleen waar of niet waar teruggeeft.
//
// En het belangrijkste verschil: het wachtwoord opent niet alleen dit SCHERM maar ook de GEGEVENS.
// De database erkent een geopende sleutel als beheerdersrecht, dus wie het wachtwoord krijgt komt
// niet op een leeg scherm terecht waar de database alles weigert.
//
// Hoe lang hij open blijft: tot middernacht van dezelfde dag. Het balie-account blijft de hele dag
// ingelogd, dus binnen die dag hoeft niemand hem twee keer in te tikken, ook niet na een herlaadbeurt
// of in een tweede tabblad. Morgen wel. Dat is de eenvoudigste vorm die bij dat account past.
function InrichtingSlot({ onOpen, scherm }){
  const T = SLOT_TEKST[scherm] || SLOT_TEKST.standaard;
  const [wachtwoord,setWachtwoord]=useState("");
  const [bezig,setBezig]=useState(false);
  const [f,setF]=useState(null);
  const [stand,setStand]=useState(null);   // null = nog aan het kijken

  useEffect(()=>{ (async()=>{
    const r = await db.rpc("sleutel_stand");
    const s = r.error ? { ingesteld:false, eigen_sleutel_open:false, echte_beheerder:false } : r.data;
    // De ROL is de deur. Een beheerder krijgt dit scherm helemaal niet te zien en tikt dus ook nooit
    // een wachtwoord in; de database laat hem op dezelfde grond door. Zou het scherm het hem toch
    // vragen, dan zou het wachtwoord een slot lijken dat het niet is.
    if(s.echte_beheerder){ onOpen(); return; }
    // Staat de sleutel van vandaag al open, dan niet opnieuw vragen. Dat is precies het geval na een
    // herlaadbeurt of in een tweede tabblad.
    if(s.eigen_sleutel_open){ onOpen(); return; }
    setStand(s);
  })(); },[onOpen]);

  async function probeer(){
    setBezig(true); setF(null);
    const r = await db.rpc("open_sleutel", { p_wachtwoord: wachtwoord });
    setBezig(false);
    if(r.error){ setF(fout(r.error)); return; }
    if(r.data !== true){ setF("Dat wachtwoord klopt niet."); return; }
    onOpen();
  }

  if(stand===null) return <div className="leeg">Even kijken…</div>;
  const ingesteld = stand.ingesteld;

  // Nog geen wachtwoord ingesteld, en je bent zelf beheerder: doorlaten, want anders kan niemand er
  // meer bij om er een in te stellen. Wel duidelijk zeggen dat het openstaat.
  if(ingesteld===false && stand.echte_beheerder) return (
    // `scherm` en niet `paneel`: `paneel` is de klasse van een dialoogblad en gaf hier een smalle
    // witte kolom links met een eigen schuifbalk. Zie de toelichting bij `.scherm` in stijl.css.
    <div className="scherm"><div className="kaart">
      <div className="kop">Dit deel staat open</div>
      <p className="bij">Er is nog geen wachtwoord ingesteld, dus er valt niets uit te lenen. Jij komt hier binnen op je beheerdersrol. Stel een wachtwoord in bij <strong>Wachtwoord</strong> in het menu als je dit deel ook aan iemand anders wilt kunnen geven.</p>
      <div className="acties"><button className="doe primair" onClick={onOpen}>Verder</button></div>
    </div></div>
  );

  // Nog geen wachtwoord ingesteld en je bent GEEN beheerder: hier niet doorlaten. Zou het scherm je
  // wel doorlaten, dan kwam je op een leeg scherm terecht, want de database weigert je alsnog. Dat
  // leest als een kapot systeem terwijl er alleen niets is ingesteld.
  if(ingesteld===false) return (
    <div className="scherm"><div className="kaart">
      <div className="kop">Hier kun je niet bij</div>
      <p className="bij">Dit deel is voor beheerders. Er is ook geen wachtwoord ingesteld waarmee je er tijdelijk in kunt, dus vraag een beheerder om er een in te stellen of om het zelf te doen.</p>
    </div></div>
  );

  return (
    <div className="poort"><div className="kaart">
      <div className="kop">{T.kop}</div>
      <p className="bij">{T.waarom}</p>
      <input type="password" value={wachtwoord} autoFocus
        onChange={e=>setWachtwoord(e.target.value)}
        onKeyDown={e=>e.key==="Enter"&&probeer()} placeholder="Wachtwoord"/>
      {f && <div className="fout">{f}</div>}
      <div className="acties">
        <button className="doe primair" onClick={probeer} disabled={bezig||!wachtwoord}>{bezig?"Even geduld…":"Openen"}</button>
      </div>
      <p className="bij">Eén keer per dag intikken is genoeg. Het geldt voor de rapportage én de inrichting, en het blijft open tot middernacht.</p>
      <p className="bij">Een beheerder komt hier zonder wachtwoord binnen. Dit wachtwoord is er voor als je er toch bij moet en er geen beheerder is.</p>
    </div></div>
  );
}

// Het wachtwoord instellen of wijzigen, en lopende sleutels intrekken.
//
// Dit scherm zit zelf ook achter het slot, dus iemand die het wachtwoord geleend heeft KOMT hier.
// Veranderen kan hij het niet: `zet_sleutelwachtwoord` en `trek_sleutels_in` vragen aan
// `is_echte_beheerder()`, die alleen naar de ROL kijkt. Zonder dat onderscheid zou de geleende
// sleutel het slot kunnen vervangen. Het scherm zegt dat ook, in plaats van een weigering af te
// wachten die niemand begrijpt.
function Wachtwoord({ toon }){
  const [stand,setStand]=useState(null);
  const [nieuw,setNieuw]=useState(""); const [nogmaals,setNogmaals]=useState("");
  const [bezig,setBezig]=useState(false); const [f,setF]=useState(null); const [ok,setOk]=useState(null);

  const laad=useCallback(async()=>{
    const r = await db.rpc("sleutel_stand");
    setStand(r.error ? { ingesteld:false, echte_beheerder:false, sleutels_open:0 } : r.data);
  },[]);
  useEffect(()=>{ laad(); },[laad]);

  async function bewaar(){
    setF(null); setOk(null);
    if(nieuw.length < 6){ setF("Kies er een van minstens zes tekens."); return; }
    if(nieuw !== nogmaals){ setF("De twee velden zijn niet gelijk."); return; }
    setBezig(true);
    const r = await db.rpc("zet_sleutelwachtwoord", { p_nieuw: nieuw });
    setBezig(false);
    if(r.error){ setF(fout(r.error)); return; }
    setNieuw(""); setNogmaals("");
    setOk("Het wachtwoord is opgeslagen. Wie het oude wachtwoord open had staan, is er nu uit.");
    toon && toon("Wachtwoord opgeslagen"); laad();
  }
  async function trekIn(){
    setF(null); setOk(null); setBezig(true);
    const r = await db.rpc("trek_sleutels_in");
    setBezig(false);
    if(r.error){ setF(fout(r.error)); return; }
    setOk(r.data === 0 ? "Er stond niemand open." : "Ingetrokken: " + r.data + " keer. Zij moeten het wachtwoord opnieuw intikken.");
    laad();
  }

  if(stand===null) return <div className="leeg">Even kijken…</div>;

  return (
    <div className="scherm"><div className="kaart">
      <div className="kop">{stand.ingesteld ? "Wachtwoord wijzigen" : "Wachtwoord instellen"}</div>
      <p className="bij">Beheerders komen sowieso bij de rapportage en de inrichting, zonder wachtwoord. Dit wachtwoord is de tweede route: geef je het aan iemand, dan komt die er ook bij, tot middernacht van diezelfde dag.</p>
      {!stand.echte_beheerder && <div className="fout">Je bent hier binnen met het wachtwoord, niet met een beheerdersrol. Het wachtwoord wijzigen kan alleen een beheerder, anders zou wie het geleend heeft het slot kunnen vervangen.</div>}
      {stand.echte_beheerder && <div>
        <div className="sub">Nieuw wachtwoord</div>
        <input type="password" value={nieuw} onChange={e=>setNieuw(e.target.value)} placeholder="minstens zes tekens"/>
        <div className="sub">Nog een keer</div>
        <input type="password" value={nogmaals} onChange={e=>setNogmaals(e.target.value)}
          onKeyDown={e=>e.key==="Enter"&&bewaar()}/>
        {f && <div className="fout">{f}</div>}
        {ok && <div className="ok">{ok}</div>}
        <div className="acties">
          <button className="doe primair" onClick={bewaar} disabled={bezig||!nieuw||!nogmaals}>{bezig?"Bezig…":"Opslaan"}</button>
          <button className="doe" onClick={trekIn} disabled={bezig}>Alle sleutels nu intrekken</button>
        </div>
        {/* Dit is de reden dat het wachtwoord bestaat: iemand krijgt het een keer, en daarna moet je
            hem er weer uit kunnen zetten. Opslaan van een nieuw wachtwoord doet dat vanzelf. */}
        <p className="bij">Op dit moment staan er <strong>{stand.sleutels_open}</strong> sleutels open. Gaat er iemand weg die het wachtwoord kent, wijzig het dan: opslaan gooit iedereen die nu binnen is er meteen uit.</p>
      </div>}
      <p className="bij"><strong>Wat dit wel en niet is.</strong> Het wachtwoord wordt versleuteld bewaard in een tabel die niemand kan uitlezen, ook niet met het publieke sleuteltje van de website, en de controle gebeurt in de database. Wat het niet is: een kluis. Wie het wachtwoord kent, kent het, en dat is precies de bedoeling ervan.</p>
      <p className="bij">Kwijt? Dan kan iemand met toegang tot de database de rij <code>inrichting</code> uit de tabel <code>sleutel</code> verwijderen. Daarna is er geen tweede route meer en komen alleen beheerders er nog in.</p>
    </div></div>
  );
}

function Sportmerk({ sport }){
  // GEEN `pil` erbij. Die klasse staat verderop in het stijlblad en overschrijft met dezelfde
  // zwaarte de achtergrond en de tekstkleur van `sport-golf` en `sport-voetgolf`, waardoor beide
  // labels bleekgrijs en vrijwel gelijk werden. Gemeten in de browser op 12 augustus, en het is
  // precies wat er op het scherm van de opdrachtgever te zien was.
  return <span className={sportKlasse(sport)}>{sportLabel(sport)}</span>;
}

function DagBeheer({ baan, datum, toon }){
  const [sjablonen,setSjablonen]=useState([]);
  const [indeling,setIndeling]=useState(null);      // huidige dagindeling van deze dag
  const [sloten,setSloten]=useState([]);            // starttijden zoals ze nu staan
  const [actieveBoekingen,setActieveBoekingen]=useState(0);
  const [gesloten,setGesloten]=useState(false);
  const [keuze,setKeuze]=useState("");
  const [genereer,setGenereer]=useState(true);
  const [opschonen,setOpschonen]=useState(false);
  const [beheerder,setBeheerder]=useState(null);
  const [bezig,setBezig]=useState(false);
  const [f,setF]=useState(null);
  const [uitslag,setUitslag]=useState(null);

  const laad=useCallback(async()=>{
    if(!baan||!datum) return;
    setF(null); setUitslag(null);
    const [sj, di, ts, bk, sl, bh] = await Promise.all([
      db.from("dagsjablonen").select("id,naam,actief").eq("actief",true).order("naam"),
      db.from("dagindeling").select("sjabloon_id,bron,aangepast_op").eq("baan_id",baan).eq("datum",datum).maybeSingle(),
      db.from("tijdsloten").select("id,starttijd,sport,max_spelers,status").eq("baan_id",baan).eq("datum",datum).order("starttijd"),
      db.from("boekingen").select("id",{count:"exact",head:true}).eq("status","actief")
        .in("tijdslot_id",(await db.from("tijdsloten").select("id").eq("baan_id",baan).eq("datum",datum)).data?.map(x=>x.id)||["00000000-0000-0000-0000-000000000000"]),
      db.from("sluitingsdagen").select("id").eq("baan_id",baan).eq("datum",datum),
      db.rpc("is_beheerder")
    ]);
    if(sj.error) setF(fout(sj.error)); else setSjablonen(sj.data||[]);
    setIndeling(di.data||null);
    setSloten(ts.data||[]);
    setActieveBoekingen(bk.count||0);
    setGesloten(((sl.data||[]).length>0));
    setBeheerder(bh.data===true);
    setKeuze(di.data ? di.data.sjabloon_id : "");
  },[baan,datum]);
  useEffect(()=>{ laad(); },[laad]);

  // Blokken van het GEKOZEN sjabloon, zodat de balie ziet wat er straks neergezet wordt voordat
  // hij op de knop drukt. Dit is de enige plek waar zichtbaar is welke sport waar vandaan komt.
  const [blokken,setBlokken]=useState([]);
  const laadBlokken=useCallback(async()=>{
    if(!keuze){ setBlokken([]); return; }
    const r=await db.from("sjabloonblokken").select("*").eq("sjabloon_id",keuze).order("volgorde").order("begintijd");
    setBlokken(r.data||[]);
  },[keuze]);
  useEffect(()=>{ laadBlokken(); },[laadBlokken]);

  // ===== Een blok wijzigen ==============================================================
  // Tot 25 augustus 2026 was dit scherm alleen-lezen: je zag wat een sjabloon neerzet maar je kon het
  // niet aanpassen, dus elke wijziging aan de indeling moest met de hand in de database. Dat is precies
  // wat de werkafspraak verbiedt (stamgegevens via een scherm, niet via losse SQL), dus staat de knop
  // er nu. De database bewaakt de grenzen zelf: max_spelers mag 1 tot en met 45 zijn, en die 45 komt
  // van footgolf (9 holes maal 5 per flight).
  //
  // Wat een wijziging WEL en NIET doet: een sjabloon is een mal, geen dag. Wie hier iets verandert,
  // verandert de dagen die daarna opnieuw ingericht worden. Dagen die al starttijden hebben blijven
  // staan zoals ze zijn, tot iemand het sjabloon opnieuw toepast met opschonen aan.
  const [bewerk,setBewerk]=useState(null);
  const [vorm,setVorm]=useState(null);
  const [blokBezig,setBlokBezig]=useState(false);
  const [blokFout,setBlokFout]=useState(null);
  const [blokOk,setBlokOk]=useState(null);
  const [wegVraag,setWegVraag]=useState(false);

  // ===== Zelf een dagsjabloon aanmaken (27 augustus 2026, verzoek van Roy) ================
  // Tot nu toe kon je alleen kiezen uit bestaande sjablonen. Nu kan de balie er zelf een
  // maken: naam invullen, aanmaken, en daarna met "Blok toevoegen" de blokken erin zetten.
  // Een vers sjabloon zonder blokken zet bij toepassen gewoon nul starttijden neer; dat is
  // geen fout maar een lege mal die nog gevuld moet worden, en dat zegt het scherm er ook bij.
  const [nieuwSjb,setNieuwSjb]=useState(false);
  const [sjbNaam,setSjbNaam]=useState("");
  const [sjbBezig,setSjbBezig]=useState(false);
  const [sjbFout,setSjbFout]=useState(null);
  async function maakSjabloon(){
    const naam=sjbNaam.trim();
    if(naam.length<3){ setSjbFout("Geef het sjabloon een naam van minstens drie tekens."); return; }
    setSjbBezig(true); setSjbFout(null);
    const r=await db.from("dagsjablonen").insert({naam:naam,actief:true}).select("id,naam").single();
    setSjbBezig(false);
    if(r.error){ setSjbFout(fout(r.error)); return; }
    setNieuwSjb(false); setSjbNaam("");
    toon && toon("Sjabloon aangemaakt");
    await laad();
    setKeuze(r.data.id);
  }

  function openBlok(b){
    setBewerk(b.id); setBlokFout(null); setBlokOk(null); setWegVraag(false);
    setVorm({ sport:b.sport, begintijd:String(b.begintijd).slice(0,5), eindtijd:String(b.eindtijd).slice(0,5),
      interval_minuten:b.interval_minuten, speelduur_minuten:b.speelduur_minuten,
      min_spelers:b.min_spelers, max_spelers:b.max_spelers });
  }
  // Een nieuw blok begint met de waarden van golf, want dat is de gewone dag. Sport staat wel open,
  // anders zou je footgolf alleen kunnen toevoegen door eerst iets anders te maken en dat om te zetten.
  function nieuwBlok(){
    setBewerk("nieuw"); setBlokFout(null); setBlokOk(null); setWegVraag(false);
    setVorm({ sport:"golf", begintijd:"09:00", eindtijd:"17:00",
      interval_minuten:9, speelduur_minuten:75, min_spelers:1, max_spelers:4 });
  }
  function veld(n,w){ setVorm(v=>({...v,[n]:w})); }

  function controleer(v){
    const tijd=/^([01]\d|2[0-3]):[0-5]\d$/;
    if(!tijd.test(v.begintijd)||!tijd.test(v.eindtijd)) return "Vul begintijd en eindtijd in als uu:mm.";
    if(v.eindtijd<=v.begintijd) return "De eindtijd moet na de begintijd liggen.";
    const g=(n,min,max,naam)=>{ const x=Number(n); if(!Number.isInteger(x)||x<min||x>max) return naam+" moet een heel getal zijn van "+min+" tot en met "+max+"."; return null; };
    return g(v.interval_minuten,1,240,"Het interval")
      || g(v.speelduur_minuten,1,600,"De speelduur")
      || g(v.min_spelers,1,45,"Het minimum aantal spelers")
      || g(v.max_spelers,1,45,"Het maximum aantal spelers")
      || (Number(v.min_spelers)>Number(v.max_spelers) ? "Het minimum kan niet hoger zijn dan het maximum." : null);
  }

  async function bewaarBlok(){
    const m=controleer(vorm);
    if(m){ setBlokFout(m); return; }
    setBlokBezig(true); setBlokFout(null);
    const waarden = {
      begintijd:vorm.begintijd, eindtijd:vorm.eindtijd,
      interval_minuten:Number(vorm.interval_minuten), speelduur_minuten:Number(vorm.speelduur_minuten),
      min_spelers:Number(vorm.min_spelers), max_spelers:Number(vorm.max_spelers)
    };
    const r = bewerk==="nieuw"
      ? await db.from("sjabloonblokken").insert({ ...waarden, sjabloon_id:keuze, sport:vorm.sport,
          volgorde:(blokken.reduce((n,b)=>Math.max(n,b.volgorde||0),0)+1) })
      : await db.from("sjabloonblokken").update(waarden).eq("id",bewerk);
    setBlokBezig(false);
    if(r.error){ setBlokFout(fout(r.error)); return; }
    const was = bewerk==="nieuw";
    setBewerk(null); setVorm(null);
    setBlokOk((was?"Blok toegevoegd. ":"Blok opgeslagen. ")+"Dit geldt voor dagen die je hierna opnieuw inricht.");
    toon && toon(was?"Blok toegevoegd":"Blok opgeslagen");
    laadBlokken();
  }

  // Weghalen kan alleen na een expliciete vraag. Een sjabloon zonder blokken is toegestaan: dan zet
  // "sjabloon toepassen" geen starttijden meer neer, en dat is precies wat je wilt bij een dag die dicht is.
  async function wegBlok(){
    setBlokBezig(true); setBlokFout(null);
    const r=await db.from("sjabloonblokken").delete().eq("id",bewerk);
    setBlokBezig(false);
    if(r.error){ setBlokFout(fout(r.error)); return; }
    setBewerk(null); setVorm(null); setWegVraag(false);
    setBlokOk("Blok weggehaald. Dit geldt voor dagen die je hierna opnieuw inricht.");
    toon && toon("Blok weggehaald");
    laadBlokken();
  }

  // Wat er NU op de dag staat, geteld per sport. Niet uit het sjabloon maar uit de echte starttijden,
  // want die twee kunnen uit elkaar lopen zodra er zonder opschonen opnieuw gegenereerd is.
  const perSport = sloten.reduce((m,s)=>{ m[s.sport]=(m[s.sport]||0)+1; return m; },{});
  const sportenNu = Object.keys(perSport).sort();
  const gewijzigd = indeling ? keuze!==indeling.sjabloon_id : !!keuze;

  async function toepassen(){
    setBezig(true); setF(null); setUitslag(null);
    const r=await db.rpc("zet_dagindeling",{p_baan_id:baan,p_datum:datum,p_sjabloon_id:keuze,
      p_genereer:genereer,p_opschonen:opschonen});
    setBezig(false);
    if(r.error){ setF(fout(r.error)); return; }
    setUitslag(r.data||{});
    toon && toon("Dagindeling opgeslagen");
    laad();
  }

  if(!baan) return <div className="leeg">Kies eerst een baan.</div>;

  return (
    <div className="paneel">
      <div className="kaart">
        <div className="kop">Zo staat deze dag nu</div>
        {gesloten && <div className="fout">Deze dag staat als sluitingsdag ingesteld. Genereren wordt dan overgeslagen.</div>}
        <div className="rij"><div className="sub">Dagsjabloon</div>
          <div>{indeling ? (sjablonen.find(s=>s.id===indeling.sjabloon_id)||{}).naam || "onbekend sjabloon" : "geen sjabloon gekoppeld"}</div></div>
        <div className="rij"><div className="sub">Ingesteld via</div>
          <div>{indeling ? (indeling.bron==="seizoen" ? "het seizoen" : "met de hand") : "n.v.t."}</div></div>
        <div className="rij"><div className="sub">Starttijden</div>
          <div>{sloten.length===0 ? "nog geen" : sloten.length}</div></div>
        <div className="rij"><div className="sub">Sporten op deze dag</div>
          <div>{sportenNu.length===0 ? "geen" : sportenNu.map(s=>
            <span key={s}><Sportmerk sport={s}/> {perSport[s]} starttijden{" "}</span>)}</div></div>
        <div className="rij"><div className="sub">Actieve boekingen</div>
          <div>{actieveBoekingen}</div></div>
      </div>

      <div className="kaart">
        <div className="kop">Dag opnieuw inrichten</div>
        {beheerder===false && <div className="fout">Je bent ingelogd als medewerker maar niet als beheerder. Je kunt hier alles zien, maar de knop hieronder zal weigeren.</div>}

        <div className="sub">Kies een dagsjabloon</div>
        <select value={keuze} onChange={e=>setKeuze(e.target.value)}>
          <option value="">Kies een sjabloon</option>
          {sjablonen.map(s=><option key={s.id} value={s.id}>{s.naam}</option>)}
        </select>
        {!nieuwSjb && <div className="acties"><button className="mini" onClick={()=>{setNieuwSjb(true);setSjbFout(null);}}>Nieuw sjabloon maken</button></div>}
        {nieuwSjb && <div className="kaart">
          <div className="kop">Nieuw dagsjabloon</div>
          <div className="rij"><div className="sub">Naam</div>
            <div><input value={sjbNaam} onChange={e=>setSjbNaam(e.target.value)} placeholder="Bijvoorbeeld: Winter, alleen golf"/></div></div>
          <div className="bij">Na het aanmaken is het sjabloon nog leeg. Zet er daarna met Blok toevoegen de blokken in (sport, tijden, spelers), en pas het dan toe op een dag.</div>
          {sjbFout && <div className="fout">{sjbFout}</div>}
          <div className="acties">
            <button className="doe primair" onClick={maakSjabloon} disabled={sjbBezig}>{sjbBezig?"Bezig…":"Sjabloon aanmaken"}</button>
            <button className="mini" onClick={()=>{setNieuwSjb(false);setSjbNaam("");setSjbFout(null);}} disabled={sjbBezig}>Annuleren</button>
          </div>
        </div>}

        {blokken.length>0 && <div className="tabelwrap"><table className="tabel"><tbody>
          {blokken.map(b=>( bewerk===b.id ? (
            <tr key={b.id}>
              <td colSpan={5}>
                <div className="kop">Blok wijzigen: <Sportmerk sport={b.sport}/></div>
                <div className="rij"><div className="sub">Eerste starttijd</div>
                  <div><input type="time" value={vorm.begintijd} onChange={e=>veld("begintijd",e.target.value)}/></div></div>
                <div className="rij"><div className="sub">Einde van het blok</div>
                  <div><input type="time" value={vorm.eindtijd} onChange={e=>veld("eindtijd",e.target.value)}/></div></div>
                <div className="rij"><div className="sub">Elke zoveel minuten een start</div>
                  <div><input type="number" min="1" max="240" value={vorm.interval_minuten} onChange={e=>veld("interval_minuten",e.target.value)}/></div></div>
                <div className="rij"><div className="sub">Speelduur in minuten</div>
                  <div><input type="number" min="1" max="600" value={vorm.speelduur_minuten} onChange={e=>veld("speelduur_minuten",e.target.value)}/></div></div>
                <div className="rij"><div className="sub">Minimaal spelers per starttijd</div>
                  <div><input type="number" min="1" max="45" value={vorm.min_spelers} onChange={e=>veld("min_spelers",e.target.value)}/></div></div>
                <div className="rij"><div className="sub">Maximaal spelers per starttijd</div>
                  <div><input type="number" min="1" max="45" value={vorm.max_spelers} onChange={e=>veld("max_spelers",e.target.value)}/></div></div>
                <div className="bij">Zet het interval gelijk aan de speelduur als iedereen tegelijk het veld op gaat en er pas een nieuwe ronde start als de vorige klaar is. Bij footgolf op negen holes is dat de shotgun-opzet: negen flights van vijf, samen vijfenveertig, een ronde per uur.</div>
                {blokFout && <div className="fout">{blokFout}</div>}
                {wegVraag
                  ? <div className="fout">Dit blok helemaal weghalen? Starttijden die er al staan blijven staan; alleen dagen die je hierna opnieuw inricht krijgen dit blok niet meer.
                      <div className="acties">
                        <button className="doe primair" onClick={wegBlok} disabled={blokBezig}>{blokBezig?"Bezig…":"Ja, weghalen"}</button>
                        <button className="mini" onClick={()=>setWegVraag(false)} disabled={blokBezig}>Nee, laat staan</button>
                      </div>
                    </div>
                  : <div className="acties">
                      <button className="doe primair" onClick={bewaarBlok} disabled={blokBezig}>{blokBezig?"Bezig…":(bewerk==="nieuw"?"Blok toevoegen":"Blok opslaan")}</button>
                      <button className="mini" onClick={()=>{setBewerk(null);setVorm(null);setBlokFout(null);setWegVraag(false);}} disabled={blokBezig}>Annuleren</button>
                      {bewerk!=="nieuw" && <button className="mini" onClick={()=>setWegVraag(true)} disabled={blokBezig}>Blok weghalen</button>}
                    </div>}
              </td>
            </tr>
          ) : (
            <tr key={b.id}>
              <td><Sportmerk sport={b.sport}/></td>
              <td>{String(b.begintijd).slice(0,5)} tot {String(b.eindtijd).slice(0,5)}</td>
              <td>elke {b.interval_minuten} min</td>
              <td>max {b.max_spelers} per starttijd</td>
              <td><button className="mini" onClick={()=>openBlok(b)} disabled={!!bewerk}>Wijzig</button></td>
            </tr>)))}
        </tbody></table></div>}
        {keuze && bewerk==="nieuw" && <div className="kaart">
          <div className="kop">Nieuw blok</div>
          <div className="rij"><div className="sub">Sport</div>
            <div><select value={vorm.sport} onChange={e=>veld("sport",e.target.value)}>
              <option value="golf">golf</option><option value="footgolf">footgolf</option></select></div></div>
          <div className="rij"><div className="sub">Eerste starttijd</div>
            <div><input type="time" value={vorm.begintijd} onChange={e=>veld("begintijd",e.target.value)}/></div></div>
          <div className="rij"><div className="sub">Einde van het blok</div>
            <div><input type="time" value={vorm.eindtijd} onChange={e=>veld("eindtijd",e.target.value)}/></div></div>
          <div className="rij"><div className="sub">Elke zoveel minuten een start</div>
            <div><input type="number" min="1" max="240" value={vorm.interval_minuten} onChange={e=>veld("interval_minuten",e.target.value)}/></div></div>
          <div className="rij"><div className="sub">Speelduur in minuten</div>
            <div><input type="number" min="1" max="600" value={vorm.speelduur_minuten} onChange={e=>veld("speelduur_minuten",e.target.value)}/></div></div>
          <div className="rij"><div className="sub">Minimaal spelers per starttijd</div>
            <div><input type="number" min="1" max="45" value={vorm.min_spelers} onChange={e=>veld("min_spelers",e.target.value)}/></div></div>
          <div className="rij"><div className="sub">Maximaal spelers per starttijd</div>
            <div><input type="number" min="1" max="45" value={vorm.max_spelers} onChange={e=>veld("max_spelers",e.target.value)}/></div></div>
          <div className="bij">Twee blokken op dezelfde tijd van dezelfde dag gaan niet samen: er is één baan. Zet ze achter elkaar.</div>
          {blokFout && <div className="fout">{blokFout}</div>}
          <div className="acties">
            <button className="doe primair" onClick={bewaarBlok} disabled={blokBezig}>{blokBezig?"Bezig…":"Blok toevoegen"}</button>
            <button className="mini" onClick={()=>{setBewerk(null);setVorm(null);setBlokFout(null);}} disabled={blokBezig}>Annuleren</button>
          </div>
        </div>}
        {keuze && !bewerk &&
          <div className="acties"><button className="mini" onClick={nieuwBlok}>Blok toevoegen</button></div>}
        {blokken.length>0 && !bewerk &&
          <div className="bij">Een sjabloon is een mal. Wat je hier wijzigt geldt voor dagen die je hierna opnieuw inricht; dagen die al starttijden hebben blijven staan zoals ze zijn.</div>}
        {blokOk && <div className="ok">{blokOk}</div>}

        <label className="sw"><input type="checkbox" checked={genereer} onChange={e=>setGenereer(e.target.checked)}/> Starttijden meteen aanmaken</label>
        <label className="sw"><input type="checkbox" checked={opschonen} onChange={e=>setOpschonen(e.target.checked)} disabled={!genereer}/> Bestaande vrije starttijden eerst weghalen</label>

        {genereer && !opschonen &&
          <div className="bij">Zonder weghalen blijven de starttijden staan die er al zijn, mét de sport die ze nu hebben. Er komen alleen tijden bij die er nog niet waren. Wil je de sport van een dag echt omzetten, zet dan het vinkje hierboven aan.</div>}
        {opschonen && actieveBoekingen>0 &&
          <div className="fout">Er staan {actieveBoekingen} actieve boekingen op deze dag. Weghalen weigert dan, en dat is met opzet: anders zou er een boeking zonder starttijd overblijven. Annuleer of verplaats die boekingen eerst.</div>}
        {opschonen && actieveBoekingen===0 &&
          <div className="bij">Alle vrije starttijden van deze dag worden weggehaald en opnieuw aangemaakt volgens het sjabloon. Er staan geen actieve boekingen, dus dit kan.</div>}

        {f && <div className="fout">{f}</div>}
        {uitslag && <div className="ok">
          Klaar. Gepland: {uitslag.starttijden_gepland ?? 0}, nieuw aangemaakt: {uitslag.starttijden_nieuw ?? 0}, weggehaald: {uitslag.verwijderd ?? 0}, nu op deze dag: {uitslag.totaal_op_dag ?? "onbekend"}.
          {uitslag.overgeslagen && <span> Overgeslagen, reden: {uitslag.overgeslagen}.</span>}
        </div>}

        <div className="acties">
          <button className="doe primair" onClick={toepassen} disabled={!keuze||bezig}>
            {bezig ? "Bezig…" : (!keuze || gewijzigd ? "Sjabloon toepassen" : "Opnieuw toepassen")}
          </button>
          <button className="mini" onClick={laad} disabled={bezig}>Opnieuw ophalen</button>
        </div>
      </div>
    </div>
  );
}
