BowlUp Tracker — Admin HelpAdmin Help Center

Documentatie voor beheerders van het BowlUp-platform. Uitsluitend toegankelijk voor admin-accounts. Documentation for BowlUp platform administrators. Only accessible to admin accounts.

Version 1.10 — May 2026 🔧 Admin only
📊
OverzichtOverview
Platform statsPlatform stats
👥
GebruikersUsers
Beheer & blokkerenManage & block
📋
Audit Log
Inlog & actiesLogins & actions
💾
Storage
MediaopslagMedia storage
🏷️
Promo Codes
KortingsbeheerDiscount mgmt
Geen resultaten gevonden. No results found.
🏠
Inleiding — Admin-portaalIntroduction — Admin Portal
Toegang, inloggen en eerste stappenAccess, login and first steps

Het admin-portaal (admin.html) is uitsluitend toegankelijk voor beheerders van het BowlUp-platform. Trainers en leerlingen hebben geen toegang tot dit portaal.

Inloggen als admin
  1. Navigeer naar /admin.html.
  2. Log in met je admin e-mailadres en wachtwoord.
  3. Bij het eerste gebruik: gebruik Setup om het eerste admin-account aan te maken.
Het admin-portaal geeft volledige toegang tot alle accounts en platform-data. Houd je inloggegevens veilig en deel ze nooit.
Vijf tabs

Het portaal is opgedeeld in vijf tabbladen: 📊 Overzicht, 👥 Gebruikers, 📋 Audit Log, 💾 Storage en 🏷️ Promo Codes. Elke tab is hieronder afzonderlijk gedocumenteerd.

The admin portal (admin.html) is only accessible to BowlUp platform administrators. Trainers and students have no access to this portal.

Log in as admin
  1. Navigate to /admin.html.
  2. Log in with your admin email address and password.
  3. First use: use Setup to create the first admin account.
The admin portal gives full access to all accounts and platform data. Keep your credentials secure and never share them.
Five tabs

The portal is divided into five tabs: 📊 Overview, 👥 Users, 📋 Audit Log, 💾 Storage and 🏷️ Promo Codes. Each tab is documented separately below.

📊
📊 Overzicht — Platform statistieken📊 Overview — Platform statistics
Realtime snapshot van het hele platformReal-time snapshot of the entire platform

De Overzicht-tab toont bij het openen van het admin-portaal een actuele samenvatting van het hele platform in drie panelen.

Statistiekkaarten
👥 Totaal trainersAantal geregistreerde trainer-accounts (inclusief geblokkeerde).
🎓 Totaal leerlingenAantal geregistreerde leerling-accounts.
⭐ Pro-abonnementenTrainers met tier = Pro.
🏆 Elite-abonnementenTrainers met tier = Elite.
🔒 Geblokkeerde accountsAccounts met blocked = 1 (inclusief tijdelijk geblokkeerde).
⏱ Actieve trialsTrainers met een actieve trial-einddatum in de toekomst.
Activiteitskaarten (30 dagen)
🆕 Nieuwe trainersTrainer-accounts aangemaakt in de afgelopen 30 dagen.
✅ Actief ingelogdTrainers die minstens één keer ingelogd zijn in de afgelopen 30 dagen.
Platform-inhoud
📅 Totaal lessenSom van alle geregistreerde lessen.
🎳 Totaal scoresSom van alle geregistreerde game-scores.
💬 Totaal berichtenSom van alle berichten (trainer ↔ leerling).

The Overview tab shows an up-to-date platform summary in three panels when you open the admin portal.

Statistic cards
👥 Total trainersNumber of registered trainer accounts (including blocked).
🎓 Total studentsNumber of registered student accounts.
⭐ Pro subscriptionsTrainers with tier = Pro.
🏆 Elite subscriptionsTrainers with tier = Elite.
🔒 Blocked accountsAccounts with blocked = 1 (including temporarily blocked).
⏱ Active trialsTrainers with an active trial end date in the future.
Activity cards (30 days)
🆕 New trainersTrainer accounts created in the last 30 days.
✅ Active loginsTrainers who logged in at least once in the last 30 days.
Platform content
📅 Total lessonsSum of all registered lessons.
🎳 Total scoresSum of all registered game scores.
💬 Total messagesSum of all messages (trainer ↔ student).
👥
👥 Gebruikers — Abonnements- en accountbeheer👥 Users — Subscription and account management
Tier wijzigen, blokkeren, trials en factuurresetsChange tier, block, trials and invoice resets

De Gebruikers-tab toont een tabel van alle accounts op het platform (trainers, leerlingen en admins). Gebruik de zoekbalk en filters bovenaan om snel het gewenste account te vinden.

Status-badges

Elke rij toont een status-badge die de huidige toestand van het account weergeeft:

  • Actief — account functioneert normaal
  • Geblokkeerd — account is geblokkeerd (met eventuele einddatum)
  • Trial t/m [datum] — trainer heeft tijdelijk Pro-toegang
Tier wijzigen

Klik op de tier-dropdown in de rij van een trainer en selecteer Starter, Pro of Elite. De wijziging wordt direct opgeslagen. De nieuwe tier is van kracht bij de volgende inlog van de trainer (het JWT wordt dan opnieuw aangemaakt).

🔒 Trainer blokkeren
  1. Klik op het 🔒 icoontje in de actiekolom van de trainer.
  2. Vul een reden in (verplicht).
  3. Stel optioneel een einddatum in. Laat leeg voor een permanente blokkering.
  4. Klik op Blokkeren.
Geblokkeerde trainers ontvangen een HTTP 403-foutmelding bij een inlogpoging. Als een einddatum is ingesteld, wordt het account automatisch gedeblokkeerd zodra die datum is verstreken.
🔓 Trainer deblokkeren

Klik op het 🔓 icoontje. De blokkering wordt direct opgeheven zonder verdere bevestiging.

⏱ Trial-periode instellen
  1. Klik op het icoontje in de actiekolom van de trainer.
  2. Selecteer een einddatum voor de trial via de datepicker.
  3. Klik op Opslaan.

Gedurende de trial krijgt de trainer automatisch Pro-functionaliteit, ongeacht het opgeslagen abonnement. De effectieve tier wordt berekend bij elke inlog. Laat de datum leeg en sla op om de trial te verwijderen.

Trial en blokkering kunnen niet gelijktijdig actief zijn. Een geblokkeerde trainer kan niet inloggen, ook niet tijdens een trial.
🗑️ Facturen resetten

Klik op het prullenbakicoontje om alle facturen van een trainer te verwijderen. Dit is een onomkeerbare actie — gebruik alleen op verzoek van de trainer of bij technische noodzaak.

Het resetten van facturen verwijdert alle factuurdocumenten. Lessen en kasboekgegevens blijven bewaard.

The Users tab shows a table of all accounts on the platform (trainers, students and admins). Use the search bar and filters at the top to quickly find the desired account.

Status badges

Each row shows a status badge indicating the current state of the account:

  • Active — account is functioning normally
  • Blocked — account is blocked (with optional end date)
  • Trial until [date] — trainer has temporary Pro access
Change tier

Click the tier dropdown in the trainer's row and select Starter, Pro or Elite. The change is saved immediately. The new tier takes effect on the trainer's next login (the JWT is then regenerated).

🔒 Block a trainer
  1. Click the 🔒 icon in the action column of the trainer.
  2. Enter a reason (required).
  3. Optionally set an end date. Leave blank for a permanent block.
  4. Click Block.
Blocked trainers receive an HTTP 403 error on login attempts. If an end date is set, the account is automatically unblocked once that date has passed.
🔓 Unblock a trainer

Click the 🔓 icon. The block is lifted immediately without further confirmation.

⏱ Set a trial period
  1. Click the icon in the action column of the trainer.
  2. Select a trial end date using the date picker.
  3. Click Save.

During the trial the trainer automatically gets Pro features, regardless of the stored subscription. The effective tier is calculated on every login. Leave the date blank and save to remove the trial.

A trial and a block cannot be active simultaneously. A blocked trainer cannot log in, even during a trial.
🗑️ Reset invoices

Click the trash icon to remove all invoices for a trainer. This is an irreversible action — use only at the trainer's request or when technically necessary.

Resetting invoices deletes all invoice documents. Lessons and financial ledger data are preserved.
📋
📋 Audit Log
Gefilterd logboek van alle platform-actiesFiltered log of all platform actions

De Audit Log-tab toont een chronologisch, pagineerd overzicht van alle login-pogingen en admin-acties op het platform. Elke pagina toont 50 regels.

Filters

Gebruik de filterbalken bovenaan de tabel om het log te verfijnen:

  • Actie-type — dropdown met alle bekende actie-codes
  • Rol — filter op trainer, student of admin
  • E-mail — vrij tekstveld, zoekt op e-mailadres
  • Datum van — toont alleen regels vanaf deze datum
Actie-codes en kleurcodering
ActieBeschrijvingKleur
login_okSuccesvolle inlog van trainer, leerling of adminGroen
login_failedInlogpoging met verkeerd wachtwoord of onbekend e-mailRood
login_blockedInlogpoging terwijl account geblokkeerd isRood
password_resetWachtwoordherstel via reset-tokenBlauw
admin_tier_changeAdmin heeft tier van een gebruiker gewijzigdOranje
admin_blockAdmin heeft een account geblokkeerdOranje
admin_unblockAdmin heeft blokkering opgehevenOranje
admin_trial_setAdmin heeft trial-einddatum ingesteld of verwijderdOranje
admin_promo_createAdmin heeft een promo code aangemaaktOranje
admin_promo_deleteAdmin heeft een promo code verwijderdOranje
Vastgelegde velden per regel
  • Datum/tijd — UTC tijdstempel
  • E-mail — e-mailadres van de betrokken gebruiker
  • Rol — trainer / student / admin
  • Actie — actie-code (zie tabel hierboven)
  • IP-adres — client-IP via X-Forwarded-For (Railway proxy)
  • Details — JSON met extra context (bijv. reden bij blokkering, nieuwe tier)
IP-adressen worden geregistreerd dankzij app.set('trust proxy', 1). Achter de Railway reverse proxy wordt het echte client-IP opgehaald uit de X-Forwarded-For header.

The Audit Log tab shows a chronological, paginated overview of all login attempts and admin actions on the platform. Each page shows 50 rows.

Filters

Use the filter bars at the top of the table to narrow down the log:

  • Action type — dropdown with all known action codes
  • Role — filter by trainer, student or admin
  • Email — free text field, searches by email address
  • Date from — shows only rows from this date onward
Action codes and colour coding
ActionDescriptionColour
login_okSuccessful login by trainer, student or adminGreen
login_failedLogin attempt with wrong password or unknown emailRed
login_blockedLogin attempt while account is blockedRed
password_resetPassword recovery via reset tokenBlue
admin_tier_changeAdmin changed a user's tierOrange
admin_blockAdmin blocked an accountOrange
admin_unblockAdmin lifted a blockOrange
admin_trial_setAdmin set or removed a trial end dateOrange
admin_promo_createAdmin created a promo codeOrange
admin_promo_deleteAdmin deleted a promo codeOrange
Recorded fields per row
  • Date/time — UTC timestamp
  • Email — email address of the user involved
  • Role — trainer / student / admin
  • Action — action code (see table above)
  • IP address — client IP via X-Forwarded-For (Railway proxy)
  • Details — JSON with extra context (e.g. reason for block, new tier)
IP addresses are logged thanks to app.set('trust proxy', 1). Behind the Railway reverse proxy, the real client IP is extracted from the X-Forwarded-For header.
💾
💾 Storage — Mediaopslag per trainerMedia storage per trainer
Overzicht van logo- en profielfoto-opslagOverview of logo and profile photo storage

De Storage-tab toont per trainer hoeveel ruimte logo's en profielfoto's innemen in de database. De tabel is gesorteerd op totaalgrootte (aflopend), zodat de zwaarste gebruikers bovenaan staan.

Totaalbalk

Bovenaan de pagina staat een samenvattingsbalk met:

  • Totale mediaopslag van het hele platform
  • Totaal aantal trainers
  • Totaal aantal leerlingen
Kolommen per trainer
  • Trainer — naam en e-mailadres
  • Logo (KB) — opslaggrootte van het trainer-logo
  • Foto's (KB) — totale opslaggrootte van alle leerlingprofielfoto's
  • Totaal (KB) — logo + foto's gecombineerd
  • Gebruik-balk — visuele indicatie relatief aan de grootste gebruiker
  • Leerlingen / Lessen / Scores — tellingen voor context
Kleurcodering
  • Normaal — minder dan 1 MB totaal
  • Oranje — totaal meer dan 1 MB
  • Rood — totaal meer dan 5 MB
Mediaopslag betreft uitsluitend logo's (logo_data) en profielfoto's (profile_photo). Deze worden als base64-tekst in de database opgeslagen. De grootte wordt gemeten via LENGTH() op die kolommen.

The Storage tab shows per trainer how much space logos and profile photos take up in the database. The table is sorted by total size (descending) so the heaviest users appear at the top.

Summary bar

At the top of the page is a summary bar showing:

  • Total media storage across the entire platform
  • Total number of trainers
  • Total number of students
Columns per trainer
  • Trainer — name and email address
  • Logo (KB) — storage size of the trainer logo
  • Photos (KB) — total storage size of all student profile photos
  • Total (KB) — logo + photos combined
  • Usage bar — visual indicator relative to the largest user
  • Students / Lessons / Scores — counts for context
Colour coding
  • Normal — less than 1 MB total
  • Orange — total more than 1 MB
  • Red — total more than 5 MB
Media storage covers only logos (logo_data) and profile photos (profile_photo). These are stored as base64 text in the database. Size is measured via LENGTH() on those columns.
🏷️
🏷️ Promo Codes — KortingsbeheerPromo Codes — Discount management
Aanmaken, bewerken en verwijderen van promotiecodesCreate, edit and delete promo codes
Nieuw / New v1.10

De Promo Codes-tab biedt volledig CRUD-beheer van promotie- en kortingscodes. Codes kunnen worden gekoppeld aan toekomstige checkout-flows.

Velden per code
VeldBeschrijvingVerplicht
CodeUnieke kortingscode. Wordt automatisch omgezet naar hoofdletters.Ja
OmschrijvingVrije tekst ter verduidelijking van het doel van de code.Nee
Typepercentage — procentuele korting (bijv. 20%)
fixed — vast bedrag in euro's (bijv. €5,00)
Ja
WaardeNumeriek kortingsbedrag of percentage.Ja
Geldig vanOptionele startdatum. Code is niet geldig vóór deze datum.Nee
Geldig t/mOptionele einddatum. Code vervalt na deze datum.Nee
Max. gebruikOptioneel maximum aantal keer dat de code gebruikt mag worden. Leeg = onbeperkt.Nee
ActiefAan/uit schakelaar. Inactieve codes worden niet geaccepteerd, ook niet binnen de geldigheidsperiode.
Status-badges
  • Actief — geldig, actief en binnen het gebruikslimiet
  • Inactief — de actief-schakelaar staat uit
  • Verlopen — de einddatum is verstreken
  • Nog niet geldig — de startdatum ligt in de toekomst
  • Max bereikt — het maximale aantal gebruik is bereikt
Code aanmaken
  1. Klik op + Nieuwe code.
  2. Vul de velden in (code en type + waarde zijn verplicht).
  3. Klik op Opslaan.
Code bewerken

Klik op het potloodicoontje in de rij van de code. Pas de gewenste velden aan en sla op. De gebruik-teller kan niet handmatig worden aangepast.

Code verwijderen

Klik op het prullenbakicoontje. De code wordt direct verwijderd. Deze actie is onomkeerbaar.

Publiek validatie-endpoint

Het endpoint GET /api/promo-codes/validate/:code controleert de geldigheid van een code zonder authenticatie. Bedoeld voor toekomstige checkout-integratie. Retourneert geldigheid, type en waarde.

The Promo Codes tab provides full CRUD management of promo and discount codes. Codes can be linked to future checkout flows.

Fields per code
FieldDescriptionRequired
CodeUnique discount code. Automatically converted to uppercase.Yes
DescriptionFree text to clarify the purpose of the code.No
Typepercentage — percentage discount (e.g. 20%)
fixed — fixed amount in euros (e.g. €5.00)
Yes
ValueNumeric discount amount or percentage.Yes
Valid fromOptional start date. Code is not valid before this date.No
Valid untilOptional end date. Code expires after this date.No
Max usesOptional maximum number of times the code can be used. Blank = unlimited.No
ActiveOn/off toggle. Inactive codes are not accepted, even within the validity period.
Status badges
  • Active — valid, active and within usage limit
  • Inactive — the active toggle is off
  • Expired — the end date has passed
  • Not yet valid — the start date is in the future
  • Max reached — the maximum usage count has been reached
Create a code
  1. Click + New code.
  2. Fill in the fields (code and type + value are required).
  3. Click Save.
Edit a code

Click the pencil icon in the code's row. Adjust the desired fields and save. The uses counter cannot be adjusted manually.

Delete a code

Click the trash icon. The code is immediately deleted. This action is irreversible.

Public validation endpoint

The endpoint GET /api/promo-codes/validate/:code checks the validity of a code without authentication. Intended for future checkout integration. Returns validity, type and value.

Veelgestelde vragenFrequently Asked Questions
Snelle antwoorden op admin-vragenQuick answers to admin questions
Hoe kan ik een trainer blokkeren?

Ga naar het tabblad 👥 Gebruikers, zoek de trainer en klik op het 🔒 icoontje. Vul een reden in en stel optioneel een einddatum in. Geblokkeerde trainers ontvangen HTTP 403 bij een inlogpoging.

Wordt een blokkering automatisch opgeheven?

Ja, als je bij het blokkeren een einddatum hebt ingesteld. Op het moment dat die datum is verstreken en de trainer probeert in te loggen, wordt de blokkering automatisch opgeheven.

Hoe stel ik een trial-periode in voor een trainer?

Klik op het ⏱ icoontje bij de trainer in de Gebruikers-tab. Selecteer een einddatum. Gedurende de trial krijgt de trainer automatisch Pro-functionaliteit, ongeacht het opgeslagen abonnement. Laat de datum leeg en sla op om de trial te verwijderen.

Wat is het verschil tussen tier wijzigen en een trial instellen?

Een tier-wijziging is permanent en wordt direct in de database opgeslagen. Een trial is tijdelijk: na de einddatum valt de trainer terug op zijn opgeslagen tier. Met een trial geef je tijdelijke Pro-toegang zonder de vaste tier te veranderen.

IP-adressen staan als leeg in de audit log

Controleer of app.set('trust proxy', 1) is geconfigureerd in server.js. Achter de Railway reverse proxy is dit vereist om het echte client-IP te lezen uit de X-Forwarded-For header. Zonder deze instelling registreert Express het interne proxy-IP.

Kan ik een promo code meer dan één keer gebruiken?

Ja, tenzij je een Max. gebruik hebt ingesteld. Als het maximum is bereikt, krijgt de code de badge Max bereikt en wordt hij niet meer geaccepteerd door het validatie-endpoint.

Hoe zie ik wie een bepaalde actie heeft uitgevoerd?

Gebruik de Audit Log-tab en filter op het e-mailadres van de admin. Alle admin_* acties worden geregistreerd met het e-mailadres van de uitvoerende admin en een details-veld met context (bijv. welke gebruiker is geblokkeerd en de opgegeven reden).

How do I block a trainer?

Go to the 👥 Users tab, find the trainer and click the 🔒 icon. Enter a reason and optionally set an end date. Blocked trainers receive HTTP 403 on login attempts.

Is a block lifted automatically?

Yes, if you set an end date when blocking. Once that date has passed and the trainer attempts to log in, the block is automatically lifted.

How do I set a trial period for a trainer?

Click the ⏱ icon next to the trainer in the Users tab. Select an end date. During the trial the trainer automatically gets Pro features, regardless of the stored subscription. Leave the date blank and save to remove the trial.

What is the difference between changing tier and setting a trial?

A tier change is permanent and stored directly in the database. A trial is temporary: after the end date the trainer reverts to their stored tier. Use a trial to give temporary Pro access without changing the permanent tier.

IP addresses appear blank in the audit log

Check that app.set('trust proxy', 1) is configured in server.js. Behind the Railway reverse proxy this is required to read the real client IP from the X-Forwarded-For header. Without this setting Express logs the internal proxy IP.

Can a promo code be used more than once?

Yes, unless you set a Max uses value. Once the maximum is reached, the code receives the Max reached badge and is no longer accepted by the validation endpoint.

How do I see who performed a specific action?

Use the Audit Log tab and filter by the admin's email address. All admin_* actions are logged with the executing admin's email address and a details field with context (e.g. which user was blocked and the reason given).