Ga naar inhoud

Hoe werkt dit: de club inrichten

Kern. Het scherm /beheer/inrichten is alleen voor de clubbeheerder. Het laat zien wat er staat, wat er nog mist en welke knoppen dat oplossen.

Waarom dit er is

De app ging er overal van uit dat een club wordt ingelezen. De dossier-import leest een map op de server, de bronkoppeling leest Sportlink en de ontwikkelseed vult de laptop. Draait geen van die drie, dan sta je ingelogd als clubbeheerder in een werkomgeving waar je niets kunt aanmaken: geen commissie, geen functie, geen persoon, geen seizoen, geen module en geen lidmaatschap. Elk scherm zei keurig “zodra de inrichting is ingelezen”, maar er was geen weg daarheen.

Dat viel pas op toen de eerste club op een eigen server kwam te staan, want een uitrol voert migraties uit en geen seeds. Dit scherm is de derde weg: met de hand, stap voor stap.

De volgorde is geen suggestie

De checklist staat in de volgorde waarin de stappen elkaar nodig hebben. Een functie kan niet zonder commissie, bezetting niet zonder personen en een kader niet zonder lopend seizoen. Sla je stap drie over, dan merk je dat pas bij stap zes, aan een keuzemenu dat leeg blijft.

  1. Een lopend seizoen. Kaders, planning, declaraties en situaties hangen er allemaal aan. Er kan er maar één actief zijn. Die schermen zoeken “het lopende seizoen”, en bij twee antwoorden kiest de database er zelf een.
  2. De startinrichting. Negen commissies, zes bestuursfuncties, de onderwerpen van de wegwijzer, de ankers van het seizoen en de eerste draaiboeken, in één keer. Daarna pas je aan wat bij jouw club anders is.
  3. De kaderlijst. Zonder mensen blijft elke functie vacant en weet de wegwijzer niemand te noemen.
  4. Wie welke functie heeft. Dit voedt de bezettingskaart, de vacatures en wie waarvoor mag tekenen.
  5. Wie er binnen mag. Zie hieronder. Dit is iets anders dan de kaderlijst.
  6. Het sportpark. Materialen, sleutels, borden en keuringen hangen aan een plek op het terrein.
  7. Wat voor de hele club geldt, en wat je gebruikt.

De startinrichting

Kies Startinrichting toepassen. Dit is de sjabloonset van de sport, uit de bibliotheek (sjabloonset, ADR 52). Hij staat sinds 13-09-2026 in een migratie en niet meer in een seed. Een seed draait alleen bij db reset op de laptop, dus op de server was de bibliotheek leeg en had deze knop niets om toe te passen.

Toepassen is idempotent op de natuurlijke sleutel (slug, naam, code). Wat er al staat, blijft staan, ook als de club het zelf heeft aangepast. Twee keer op de knop drukken voegt dus niets dubbel toe, en na een nieuwe versie van de bibliotheek kun je hem opnieuw toepassen.

Ook een functie die in de bibliotheek later een andere naam kreeg, telt als aanwezig. De functie van de commissieleider heet in de bibliotheek nu “Commissieleider” met de naam van de commissie erachter, bijvoorbeeld “Commissieleider sponsorcommissie”. Heeft je club de set eerder toegepast met een oudere naam, dan blijft die naam staan en komt er geen tweede functie naast. De app hernoemt niets zelf. Wil je de nieuwe naam, open dan de commissie, ga naar Bezetting en kies Bewerk functie.

Elke commissie krijgt haar modules mee

De set zegt welke commissies er zijn en ook welk deel van de app ze bedienen: de sponsorcommissie krijgt sponsoring, de kantinecommissie canteen en shifts, de terreincommissie borden_uitvoering en locaties_beheer. Die rijen komen in commissie_module. Elke mag_*-functie kijkt via app.heeft_module naar die rijen.

Tot 13-09-2026 deed de set dat niet, en dat was niet te zien. Er ging niets stuk: de sponsorcommissie kwam gewoon op een sponsorscherm zonder contracten, want RLS geeft nul rijen aan wie niets mag. Een ingerichte club leek daardoor leeg, terwijl er niets leeg was. Zoek je het volgende gat, kijk dan naar de rechten die eruit volgen en niet naar de rijen.

Het bestuur en de beheerder staan hier los van. Zij krijgen via hun rol toegang overal waar de club de module afneemt. Een club waar alleen het bestuur inlogt, merkt van de commissierechten dus nooit iets. Daarom viel het zo laat op.

Kaderlijst en toegang zijn twee dingen

In de kaderlijst staan mensen. Die hoeven nooit in te loggen. Een coördinator die alleen telefonisch bereikbaar is, staat er gewoon in.

Toegang is een uitnodiging op een e-mailadres. Zo gaat dat:

  1. De beheerder legt een uitnodiging klaar: adres, rol en desgewenst de persoon uit de kaderlijst waar dit account bij hoort.
  2. De uitgenodigde vraagt zelf op de inlogpagina een link aan op dat adres. Zijn account ontstaat daar, bij GoTrue, zoals bij iedereen.
  3. Bij die eerste binnenkomst wordt de uitnodiging verzilverd: hij krijgt zijn lidmaatschap, en als er een persoon aan hing, wordt die aan zijn account gekoppeld.

Vanuit dit scherm gaat er geen mail de deur uit. Dat is de eenvoudigste vorm. De uitnodiging is klaargelegd en de uitgenodigde gebruikt de gewone inlogweg.

Waarom hier geen account wordt aangemaakt: GoTrue houdt naast auth.users ook auth.identities bij. Een account dat daarbuiten om ontstaat, mist die rij. Dat lijkt te werken tot iemand van inlogmethode wisselt.

Wat we gebruiken

Welke modules aanstaan, bepaalt wat de club kan. Een nieuwe club krijgt ze allemaal aan en zet zelf uit wat ze niet gebruikt.

Uitzetten sluit het menu, het scherm, de schrijfacties en de policies eronder. Ook een bestuurslid of beheerder komt er dan niet meer in. Dat is het verschil met vroeger, toen de schakelaar alleen opsloeg en verder niets deed (ADR 69).

Uitzetten verwijdert niets. Wat er staat, blijft staan en komt terug zodra je de module weer aanzet. Ook een module die je een seizoen overslaat, is dus veilig.

Twee dingen om te weten:

  • Een commissierecht telt alleen zolang de module eronder aanstaat. De kledingcommissie houdt haar recht, maar merkt het pas weer als kleding aangaat.
  • Een recht kan bij twee modules horen. declaraties_beheer hoort bij realisatie én bij declaraties. Het telt zolang één van die twee aanstaat.

Waar de rechten vandaan komen

Voor dit scherm is geen enkele nieuwe uitzondering gemaakt. Commissie, functie, persoon, seizoen en organisatie_module mocht de beheerder allang schrijven. Er ontbrak alleen een scherm. De twee dingen die wel een functie nodig hadden, staan in migratie 20260913250000_startinrichting.sql: de stand van de inrichting (een telling over tabellen waar niet iedereen bij mag) en het verzilveren van een uitnodiging (dat maakt een lidmaatschap bij een club waar de aanvrager op dat moment nog geen lid van is).

Een club vullen vanaf de opdrachtregel

Bestaat de inrichting van een club al als bestand, bijvoorbeeld uit de kennisbank van die club, dan vul je hem vanaf de opdrachtregel. Verzonnen voorbeeldvulling staat apart in supabase/seed/voorbeeld/ en komt nooit op een draaiende club. Het script scripts/club-vullen.mjs brengt de inrichting van een club naar een draaiende server. Het schrijft SQL naar stdout en verandert zelf niets:

Terminal window
node scripts/club-vullen.mjs --org <uuid> --ik "Jouw Naam" > /tmp/club.sql

Wat er meegaat: de commissies, de functies, de kaderlijst, het sportpark, de sponsors, de borden, het logo, het kleurpalet en de kerngegevens uit de statuten. Wat er niet in gaat: de verzonnen KvK-, btw- en bankgegevens die in de seed staan om een factuur er lokaal goed uit te laten zien. Die horen niet op een echte factuur. De penningmeester vult ze zelf in.

Op de server draait deploy/club-vullen.sh dat bestand in één transactie. Er zit een grendel voor. Het script weigert op een club die al commissies heeft, op een club zonder lopend seizoen en in een database waar de vaste sleutels van deze seed al bestaan. Deze seeds zijn niet idempotent, en een halve tweede ronde is erger dan geen tweede ronde.

De demo-omgeving

Op demo.<domein> staat de voordeur van de demo (ADR 56). Met één knop krijgt de bezoeker zijn eigen wegwerpclub op een eigen subdomein. Die club is vier uur houdbaar en heeft zes petten om tussen te wisselen.

Die club is bewust geen lege huls. Hij draait drie seizoenen: twee afgesloten en een lopend, elk met een eigen kader, eigen uitgaven en eigen sponsorinkomsten. Daarom is app.demo_inrichten zo groot. Zonder geschiedenis laat je alleen schermen zien die technisch werken. Met geschiedenis laat je zien waar ze voor zijn:

  • budget naast realisatie, met een terrein dat vorig seizoen uitschoot door stormschade;
  • een hoofdsponsorcontract dat aan het eind van dit seizoen afloopt;
  • een melding over de bardiensten die al drie weken open staat;
  • een vacature in het bestuur die al zeven maanden loopt;
  • een bord dat is gedrukt maar nog niet hangt;
  • drie artikelen onder de minimumvoorraad en twee keuringen die bijna verlopen;
  • twintig sponsors op veertig bordposities, waarvan er dertien nog te verkopen zijn;
  • vier documenten in vier stadia: klaar om aan te bieden, wachtend op de sponsor, wachtend op de club, en rond. Daar kan een bezoeker zelf doorheen: in een democlub staan de tekenlinks op het scherm, omdat er naar een verzonnen adres nooit mail aankomt.

Dat laatste staat in app.demo_aanvullen, naast app.demo_inrichten en niet erin. Die functie is twaalfhonderd regels. Er een blok bij zetten betekent hem in zijn geheel opnieuw opschrijven in een nieuwe migratie, en dan is elke regel een kans op een tikfout.

Alles erin is verzonnen. SV De Beemd bestaat niet, en geen van de namen, bedrijven of bedragen hoort bij een echte vereniging. Dat is de eis: een bezoeker van de demo mag nooit het kaderlid van een bestaande club tegenkomen.

Aanzetten op een server: zie stap 7c van deploy/README.md.

Een nieuw seizoen en tweestapsverificatie

Een nieuw actief seizoen vervangt het vorige in één handeling. Mislukt het aanmaken, bijvoorbeeld door een al gebruikte naam, dan blijft het vorige seizoen actief. Gebruik volledige kalenderdatums. Een ongeldige datum wordt vóór het opslaan afgewezen.

Club- en platformbeheerders bevestigen iedere nieuwe sessie met hun authenticator. Een eerder ingestelde authenticator is daarvoor niet voldoende. Op Account → Beveiliging voer je de huidige zescijferige code in. Bij een foute code blijft de ingestelde factor behouden, zodat je opnieuw kunt proberen. De tijdelijke democlub houdt haar uitzondering.