Een interactief dashboard voor het monitoren van Wet Open Overheid (WOO) verzoeken van Gemeente Utrecht en Provincie Flevoland met keuze tussen mock database, Erlang/OTP backend en PostgreSQL backend met moderne database features.
Het dashboard is live beschikbaar op: https://terminal-woo.github.io/woo-dashboard/
Note: GitHub Pages gebruikt CDN caching. Het kan 10-60 minuten duren voordat updates zichtbaar zijn. Voor directe updates: gebruik incognito mode of hard refresh (Ctrl+Shift+R).
WOO Dashboard is een modern React-based dashboard dat statistieken en status updates van WOO-verzoeken visualiseert. Het systeem combineert drie backend opties met volledige Document Management Systems en event streaming:
- Mock Backend: Pure JavaScript in-memory database (TypeScript)
- Erlang Backend: Echte Erlang/OTP applicatie met gen_server, supervisors en REST API
- PostgreSQL Backend: Modern database systeem met LISTEN/NOTIFY, JSONB, full-text search en actor model
- Paperless-ngx DMS: Lichtgewicht open-source document management met OCR en tagging
- Alfresco DMS: Enterprise ECM platform met workflow management
- DMS Simulator: Interactieve document generator met realistische PDFs (6 types)
- NATS JetStream: Event streaming platform met 7-dagen persistente opslag
- Event Consumer API: REST API voor event querying en statistieken
- Event Stream Viewer: Real-time dashboard component voor live event monitoring
- π Triple Backend Architecture: Switch tussen mock, Erlang en PostgreSQL backend via UI
- β‘ Erlang/OTP Backend: Volledige OTP applicatie met gen_server, gen_event, supervisor tree
- πΎ Mock Database: Pure JavaScript in-memory database met 24 realistische documenten
- π Erlang Actor System: Fault-tolerant event handling met supervisors en message passing
- π 6-Stage Workflow: Lineaire status progressie met cyclische herstart
- Ontvangen β In behandeling β 1e Concept β 2e Concept β Definitief β Gepubliceerd
- β±οΈ Real-time Simulatie: Automatische doorloop van document statussen (2 seconden interval)
- π‘ Live Event Feed: Real-time notificaties van alle status wijzigingen via Erlang actors
- π₯§ Detailed Status Visualization: Pie chart toont alle 6 workflow statussen met unieke kleuren
- ποΈ Organisatie Filtering: Gescheiden data voor gemeente (12 documenten) en provincie (12 documenten)
- π Paperless-ngx: Lichtgewicht DMS met PostgreSQL 18 + MinIO S3 storage
- π’ Alfresco: Enterprise ECM met Digital Workspace en Share UI
- π DMS Simulator: Upload 1-50 realistische PDFs (besluit, advies, brief, notitie, rapportage, contract)
- π¦ MinIO Storage: S3-compatible lokale object storage (NIET AWS cloud!)
- ποΈ PostgreSQL Metadata: Automatische tracking van buckets, objects, en statistieken
- π Multi-DMS Upload: Upload tegelijk naar Paperless Γ©n Alfresco
- π‘ NATS JetStream: Persistent event storage met 7-dagen retentie
- π Event Stream Viewer: Real-time dashboard component met filters en statistieken
- π Event Analytics: Totaal events, per systeem, gemiddelde upload tijd
- π― Complete Audit Trail: Alle document.uploaded, document.updated, document.deleted events
- π REST API: Event Consumer API op port 3002 met SSE support
- π Automated Startup:
start-all.shscript voor complete systeem - π Verification Tool:
verify-system.shvoor health checks - π Comprehensive Docs: COMPLETE_SYSTEM_GUIDE.md met alle features
- π³ Docker Compose: Alles draait in containers (PostgreSQL 18, MinIO, NATS, DMS)
- π± Responsive Design: Werkt op desktop, tablet en mobile
- React 18 - UI framework met hooks (useState, useEffect)
- TypeScript - Type-safe development met strikte typing
- Recharts - Data visualisatie voor bar charts en pie charts
- Vite - Build tool en development server
- CSS3 - Styling met custom properties en responsive grid
- In-Memory Database - Pure JavaScript Map-based storage
- Erlang-Inspired Actors - Actor model simulatie in TypeScript
- 24 Sample Documents - Pre-loaded realistic data
- Client-Side Only - No server required
- Erlang/OTP 24+ - Production-grade runtime
- Cowboy Web Server - HTTP/REST API server
- ETS Database - In-memory Erlang Term Storage
- Gen_Server - Document management (woo_document_manager)
- Gen_Event - Event notification system (woo_event_manager)
- Supervisor - Fault-tolerant process supervision
- Rebar3 - Build tool en dependency management
- PostgreSQL 14+ - Production-grade relational database
- Node.js/Fastify - Modern REST API server
- LISTEN/NOTIFY - Real-time pub/sub for event streaming
- JSONB - Flexible JSON storage with indexing
- Full-text Search - Dutch language text search with tsvector
- Materialized Views - Fast aggregated statistics
- Triggers - Automatic audit trails and notifications
- Actor System - Erlang-inspired actors backed by PostgreSQL
Het systeem gebruikt een in-memory mock database (src/mockDatabase.ts) met Map-based storage:
interface WOORequest {
id: string; // Document ID (bijv. WOO-UTR-2024-001)
title: string; // Titel van het verzoek
status: WOOStatus; // Huidige status (6 mogelijke waarden)
submittedDate: string; // Datum van indiening (ISO format)
organization: string; // Gemeente Utrecht / Provincie Flevoland
organizationType: OrganizationType; // 'gemeente' | 'provincie'
category: string; // Categorie (bijv. Ruimtelijke ordening)
subject: string; // Onderwerp omschrijving
requester: string; // Indiener naam
handler: string; // Behandelaar/afdeling
decidedDate?: string; // Datum van besluit (optioneel)
lastModified: string; // Laatste wijziging timestamp
}type WOOStatus =
| "Ontvangen" // π Oranje - Document binnengekomen
| "In behandeling" // π΅ Blauw - Wordt beoordeeld
| "1e Concept" // π£ Paars - Eerste conceptversie
| "2e Concept" // π΄ Roze - Tweede conceptversie
| "Definitief" // π· Cyaan - Definitieve versie
| "Gepubliceerd"; // π’ Groen - Openbaar gemaaktDe mock database service (mockDatabaseService) biedt:
- getAll(): Promise<WOORequest[]> // Alle documenten ophalen
- getById(id: string): Promise<WOORequest | null> // Specifiek document ophalen
- getByOrganization(org: string): Promise<WOORequest[]> // Filter op organisatie
- getByStatus(status: WOOStatus): Promise<WOORequest[]> // Filter op status
- update(id: string, newStatus: WOOStatus): Promise<void> // Status update
- getStatistics(): Promise<Statistics> // Statistieken berekenenHet systeem bevat 24 realistische documenten, verdeeld over beide organisaties en alle statussen:
- Ontvangen (2): Nieuwbouwproject Merwedekanaal, Duurzaamheidsplan gemeentelijke gebouwen
- In behandeling (2): Verkeersplan binnenstad, Subsidieregeling maatschappelijke initiatieven
- 1e Concept (2): Subsidieverlening culturele instellingen, Nota parkeernormen 2024
- 2e Concept (2): Omgevingsvisie Utrecht 2040, Herinrichtingsplan Vredenburg
- Definitief (2): Contracten afvalverwerking, Beleidsnota jeugdhulpverlening
- Gepubliceerd (2): Aanbesteding openbaar vervoer, Gemeentelijke begroting 2024
- Ontvangen (2): Subsidieregeling duurzame landbouw, Provinciaal waterplan 2025
- In behandeling (2): Windmolenpark Noordoostpolder, Subsidie natuurbeheer landgoederen
- 1e Concept (2): Reconstructie N23 wegverbreding, Visie recreatie en toerisme
- 2e Concept (2): Stikstofrapportage landbouw, Ontwikkelplan bedrijventerrein Lelystad Airport
- Definitief (2): Natuurontwikkeling Oostvaardersplassen, Omgevingsvergunning zonnepark Zeewolde
- Gepubliceerd (2): N23 reconstructie projectplan, Energietransitie roadmap 2030
Elke document bevat realistische categorieΓ«n zoals:
- Ruimtelijke ordening
- Milieu en duurzaamheid
- Verkeer en vervoer
- Subsidies en financiΓ«n
- Cultuur en onderwijs
- Natuur en landschap
woo-dashboard/
βββ src/
β βββ components/
β β βββ StatsCard.tsx # Statistiek cards
β β βββ RequestsTable.tsx # Documenten tabel met status badges
β β βββ ActivityFeed.tsx # Live event feed (Erlang actor subscriber)
β βββ App.tsx # Hoofd applicatie component
β βββ App.css # Applicatie styling met status colors
β βββ types.ts # TypeScript type definities
β βββ data.ts # Data utility functies
β β βββ calculateStats() # Bereken dashboard statistieken
β β βββ getDetailedStatusDistribution() # Pie chart data (alle 6 statussen)
β β βββ getMonthlyData() # Maandelijkse trend data
β β βββ getOrganizationStats() # Per-organisatie statistieken
β βββ erlangActorSystem.ts # Complete Erlang/OTP implementatie
β β βββ Actor # Process met PID, mailbox, message handler
β β βββ Supervisor # OTP supervisor met restart strategies
β β βββ EventManager # Gen_event voor pub/sub
β β βββ ProcessRegistry # Named process registration
β β βββ Application # Application controller
β βββ erlangSimulatorV2.ts # V2 met mock database integratie
β β βββ initialize() # Setup database en actors
β β βββ start() # Start simulatie met interval
β β βββ stop() # Stop simulatie
β β βββ getStatistics() # Ophalen statistieken
β βββ mockDatabase.ts # In-memory database met 24 documenten
β β βββ mockDatabaseService # CRUD operations en queries
β βββ databaseActor.ts # Legacy SQLite actor (deprecated)
β βββ erlangSimulator.ts # V1 simulator (deprecated)
β βββ eventSystem.ts # Legacy event management (deprecated)
β βββ statusSimulator.ts # Legacy status simulator (deprecated)
β βββ documentGenerator.ts # Document generator utilities
β βββ main.tsx # App entry point
βββ .github/
β βββ workflows/
β βββ deploy.yml # GitHub Actions automatic deployment
βββ index.html
βββ package.json
βββ tsconfig.json
βββ vite.config.ts # Base path: /woo-dashboard/
βββ README.md
Dit project implementeert een Erlang/OTP-geΓ―nspireerd Actor Model voor robuuste event handling en concurrency. Erlang is wereldberoemd voor zijn betrouwbaarheid in telecom systemen en distributed systems.
Erlang's filosofie ("let it crash", actor model, supervision trees) biedt:
- Isolatie: Elke actor heeft zijn eigen state, geen gedeeld geheugen
- Message Passing: Actors communiceren via immutable messages
- Fault Tolerance: Supervisors kunnen gefaalde actors herstarten
- Concurrency: Actors verwerken messages asynchroon maar sequentieel
- No Race Conditions: Sequential mailbox processing voorkomt race conditions
1. Actor System (src/erlangActorSystem.ts)
Implementeert Erlang concepten in TypeScript:
// Actor: Equivalent van Erlang process met PID, mailbox en message handler
class Actor {
private pid: PID; // Unieke process identifier (bijv. <0.42.0>)
private mailbox: Message[] = []; // Message queue
private isProcessing = false; // Processing lock
private state: any = {}; // Actor state
send(message: Message): void // Stuur bericht naar mailbox
private processMailbox(): Promise<void> // Verwerk berichten sequentieel
}
// Supervisor: OTP Supervisor pattern voor fault tolerance
class Supervisor {
private children: Map<PID, Actor>;
private strategy: RestartStrategy; // permanent | temporary | transient
startChild(childSpec): Promise<PID>
restartChild(pid: PID): Promise<void>
stopChild(pid: PID): Promise<void>
}
// EventManager: Gen_event behavior voor pub/sub
class EventManager {
private handlers: Map<string, Actor>;
private eventHistory: Event[] = [];
addHandler(name: string, actor: Actor): void
notify(event: Event): Promise<void> // Broadcast naar alle handlers
}
// ProcessRegistry: Named process registration
class ProcessRegistry {
private registry: Map<string, PID>;
register(name: string, pid: PID): void
whereis(name: string): PID | undefined
}2. Erlang Simulator V2 (src/erlangSimulatorV2.ts)
Document lifecycle management met actors en mock database integratie:
const ORGANIZATIONS: Organization[] = [
{
id: "gemeente-utrecht",
name: "Gemeente Utrecht",
type: "gemeente",
statusWorkflow: [
"Ontvangen",
"In behandeling",
"1e Concept",
"2e Concept",
"Definitief",
"Gepubliceerd",
],
},
{
id: "provincie-flevoland",
name: "Provincie Flevoland",
type: "provincie",
statusWorkflow: [
"Ontvangen",
"In behandeling",
"1e Concept",
"2e Concept",
"Definitief",
"Gepubliceerd",
],
},
];Actors in het systeem:
- Ticker Actor: Timer proces dat periodieke tick messages stuurt (2 seconden interval)
- Document Manager Actor: GenServer-style state management voor document updates
- Event Publisher Actor: Gen_event voor event distribution naar subscribers
Status Progressie:
- Start met 24 documenten verdeeld over alle statussen
- Elke 2 seconden: selecteer willekeurig document dat nog niet "Gepubliceerd" is
- Move document naar volgende status in workflow
- Broadcast status change event naar alle subscribers
- Als alle documenten "Gepubliceerd" zijn: reset 1 willekeurig document naar "Ontvangen"
3. Activity Feed met Actors (src/components/ActivityFeed.tsx)
useEffect(() => {
// Spawn subscriber actor bij component mount
const subscriberActor = application.spawn(async (message) => {
if (message.type === "status_change") {
setEvents((prev) => [...message.data, ...prev].slice(0, 10));
}
});
// Registreer bij EventManager
eventManager.addHandler("activity_feed", subscriberActor);
// Cleanup actor bij unmount
return () => {
application.kill(subscriberActor.getPid());
};
}, []);Erlang-style message tuples (als TypeScript types):
type Message =
| { type: 'status_change', data: StatusChangeData }
| { type: 'new_document', data: DocumentData }
| { type: 'tick', data: { timestamp: number } }
| { type: 'subscribe', data: { callback: Function } }
| { type: 'shutdown' }
| { type: 'restart' }- Testbaarheid: Actors zijn geΓ―soleerd en makkelijk te testen
- Debugbaarheid: Message flow is traceable en inspectable
- Schaalbaarheid: Easy toe te voegen nieuwe actors voor nieuwe features
- Betrouwbaarheid: Supervisors vangen crashes op en herstarten actors
- Clean Code: Separation of concerns via message passing
- No Race Conditions: Sequential message processing in actors
- Fault Tolerance: "Let it crash" philosophy met automatic recovery
Het project bevat een volledige Erlang/OTP backend implementatie in erlang-backend/:
erlang-backend/
βββ apps/woo_backend/src/
β βββ woo_backend_app.erl # Application behavior
β βββ woo_sup.erl # Top-level supervisor
β βββ woo_document_manager.erl # Gen_Server (documents)
β βββ woo_event_manager.erl # Gen_Event (events)
β βββ woo_simulation_server.erl # Gen_Server (simulation)
β βββ woo_http_handler.erl # Cowboy HTTP handler
βββ config/
β βββ sys.config # System configuration
β βββ vm.args # VM arguments
βββ rebar.config # Dependencies & build config
-
Application (woo_backend_app)
- Lifecycle management
- Start supervisor en HTTP server
-
Supervisor (woo_sup)
- Strategy:
one_for_one - Restart crashed processes automatisch
- Intensity: 10 restarts / 60 seconden
- Strategy:
-
Gen_Server (woo_document_manager)
- Stateful document management
- ETS table voor snelle storage
- Synchrone calls voor CRUD operations
-
Gen_Event (woo_event_manager)
- Event notification systeem
- Pub/sub voor status changes
- Event history buffer (50 events)
-
Gen_Server (woo_simulation_server)
- Automatische status progressie
- Timer-based ticks (2 seconden)
- Start/stop API
De Erlang backend biedt een volledige REST API:
Documents
GET /api/documents- Alle documentenGET /api/documents/:id- Specifiek documentPOST /api/documents/:id/status- Update status{"status": "In behandeling"}
Statistics
GET /api/statistics- Algemene statsGET /api/statistics/utrecht- Gemeente Utrecht statsGET /api/statistics/flevoland- Provincie Flevoland stats
Simulation
POST /api/simulation/start- Start simulatiePOST /api/simulation/stop- Stop simulatie
Health
GET /api/health- Health check + simulation status
Voorbeeld Response:
{
"id": "WOO-UTR-2024-001",
"title": "Informatie over nieuwbouwproject Merwedekanaal",
"status": "In behandeling",
"organization": "Gemeente Utrecht",
"organizationType": "gemeente",
"category": "Ruimtelijke ordening",
"lastModified": "2024-10-30T14:23:15Z"
}- β Fault Tolerance - Supervisor tree restart crashes
- β Concurrency - Handles thousands of concurrent requests
- β Hot Code Reloading - Update code zonder downtime
- β ETS Storage - Microsecond read/write latency
- β CORS Support - Frontend kan backend aanroepen
- β JSON API - jsx library voor encoding/decoding
- β Cowboy Web Server - Production-ready HTTP server
De frontend gebruikt erlangBackendService.ts om met de backend te communiceren:
// Fetch alle documenten
const docs = await erlangBackendService.getAll();
// Update document status
await erlangBackendService.update("WOO-UTR-2024-001", "Definitief");
// Start simulatie op backend
await erlangBackendService.startSimulation();
// Health check
const health = await erlangBackendService.healthCheck();De backendService.ts abstraction layer maakt switching mogelijk:
// Switch naar Erlang backend
backendService.switchBackend("erlang");
// Check of backend beschikbaar is
const available = await backendService.checkErlangBackendAvailable();Zie erlang-backend/README.md voor volledige documentatie.
De PostgreSQL backend combineert Erlang actor model patronen met moderne PostgreSQL features voor een robuuste, schaalbare oplossing.
Het actor systeem is volledig geΓ―ntegreerd met PostgreSQL:
// Actor backed by PostgreSQL connection pool
class Actor extends EventEmitter {
private pool: Pool; // PostgreSQL connection pool
private listenClient?: PoolClient; // Dedicated client for LISTEN
async send(message: Message): Promise<any> {
// Process message in transaction
const client = await this.pool.connect();
await client.query('BEGIN');
const result = await this.behavior(this.state, message);
await client.query('COMMIT');
client.release();
return result;
}
async listen(channel: string): Promise<void> {
// Subscribe to PostgreSQL NOTIFY
this.listenClient = await this.pool.connect();
await this.listenClient.query(`LISTEN ${channel}`);
this.listenClient.on('notification', (msg) => {
this.emit('postgres_notify', {
channel: msg.channel,
payload: JSON.parse(msg.payload)
});
});
}
}1. LISTEN/NOTIFY voor Real-time Events
-- Trigger sends NOTIFY on status change
CREATE TRIGGER woo_request_status_change
AFTER UPDATE ON woo_requests
FOR EACH ROW
WHEN (OLD.status IS DISTINCT FROM NEW.status)
EXECUTE FUNCTION notify_status_change();
-- Function sends notification
CREATE FUNCTION notify_status_change() RETURNS trigger AS $$
BEGIN
PERFORM pg_notify('status_change',
json_build_object(
'document_id', NEW.id,
'old_status', OLD.status,
'new_status', NEW.status
)::text
);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;2. Full-text Search (Nederlands)
-- Auto-generated search vector
search_vector tsvector GENERATED ALWAYS AS (
setweight(to_tsvector('dutch', coalesce(title, '')), 'A') ||
setweight(to_tsvector('dutch', coalesce(subject, '')), 'B')
) STORED
-- GIN index voor snelle search
CREATE INDEX idx_woo_requests_search ON woo_requests USING GIN(search_vector);
-- Search query
SELECT *, ts_rank(search_vector, query) as rank
FROM woo_requests,
websearch_to_tsquery('dutch', 'bestemmingsplan') query
WHERE search_vector @@ query
ORDER BY rank DESC;3. JSONB voor Flexible Metadata
-- JSONB column met GIN index
metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
CREATE INDEX idx_woo_requests_metadata ON woo_requests USING GIN(metadata);
-- Query JSONB
SELECT * FROM woo_requests
WHERE metadata @> '{"theme": "ruimtelijke_ordening"}'::jsonb;4. Materialized Views voor Statistics
CREATE MATERIALIZED VIEW statistics_summary AS
SELECT
status,
COUNT(*) as count,
AVG(EXTRACT(EPOCH FROM (updated_at - created_at)) / 86400) as avg_days_in_status
FROM woo_requests
GROUP BY status;
-- Refresh when needed
REFRESH MATERIALIZED VIEW statistics_summary;- β ACID Transactions - Guaranteed data consistency
- β Real-time LISTEN/NOTIFY - Push-based event streaming
- β Full-text Search - Dutch language support with tsvector
- β JSONB Indexing - Fast flexible schema queries
- β Materialized Views - Pre-computed statistics
- β Automatic Audit Trail - status_history table via triggers
- β Actor Model - Erlang-inspired patterns with PostgreSQL backing
- β Connection Pooling - Efficient resource management
- β Concurrent Access - Row-level locking
// Fetch alle documenten
const docs = await postgresBackendService.getAll();
// Full-text search
const results = await postgresBackendService.search("bestemmingsplan");
// Update status (triggers NOTIFY)
await postgresBackendService.update("1", "Definitief");
// Start simulatie
await postgresBackendService.startSimulation();
// Health check
const available = await postgresBackendService.checkHealth();Zie postgres-backend/README.md voor volledige documentatie.
De pie chart (src/App.tsx) gebruikt getDetailedStatusDistribution() om alle 6 workflow statussen weer te geven:
// Oude implementatie (3 statussen):
const statusDistribution = getStatusDistribution(stats);
// Resultaat: Ontvangen, In Behandeling, Gepubliceerd
// Nieuwe implementatie (6 statussen):
const statusDistribution = getDetailedStatusDistribution(requests);
// Resultaat: Alle 6 individuele statussen met kleurenOutput in pie chart:
- π Ontvangen (#d97706) - 4 documenten (16.7%)
- π΅ In behandeling (#107abe) - 4 documenten (16.7%)
- π£ 1e Concept (#9333ea) - 4 documenten (16.7%)
- π΄ 2e Concept (#db2777) - 4 documenten (16.7%)
- π· Definitief (#0891b2) - 4 documenten (16.7%)
- π’ Gepubliceerd (#16a34a) - 4 documenten (16.7%)
Frontend:
- Node.js 20 of hoger
- npm
Erlang Backend (optioneel):
- Erlang/OTP 24 of hoger
- Rebar3
# Clone de repository
git clone https://github.com/YOUR_USERNAME/woo-dashboard.git
cd woo-dashboard
# Installeer dependencies
npm install
# Start development server
npm run devDe applicatie is nu beschikbaar op http://localhost:5173/woo-dashboard/
Het dashboard start standaard met de Mock Backend (TypeScript in-memory database).
Voor het gebruik van de echte Erlang/OTP backend:
1. Installeer Erlang/OTP en Rebar3
# macOS
brew install erlang rebar3
# Ubuntu/Debian
sudo apt-get update
sudo apt-get install erlang rebar32. Start de Erlang backend
Open een nieuwe terminal:
cd erlang-backend
rebar3 get-deps
rebar3 compile
rebar3 shellDe backend luistert nu op http://localhost:8080
3. Start de frontend
In een andere terminal:
cd woo-dashboard
npm run dev4. Switch naar Erlang backend
- Open
http://localhost:5173/woo-dashboard/in browser - Klik op "Erlang" button in de rechterbovenhoek van de header
- De groene indicator toont dat de backend beschikbaar is
- Pagina wordt automatisch herladen met Erlang backend
Voor het gebruik van de PostgreSQL backend met moderne database features:
1. Installeer PostgreSQL
# macOS
brew install postgresql@14
brew services start postgresql@14
# Ubuntu/Debian
sudo apt-get update
sudo apt-get install postgresql-14
sudo systemctl start postgresql2. Create database
createdb woo_dashboard3. Configureer environment
cd postgres-backend
cp .env.example .env
# Edit .env met je PostgreSQL credentials4. Start de PostgreSQL backend
cd postgres-backend
npm install
npm run devDe backend luistert nu op http://localhost:8081
5. Switch naar PostgreSQL backend
- Open
http://localhost:5173/woo-dashboard/in browser - Klik op "π PostgreSQL" button in de header
- De groene indicator toont dat de backend beschikbaar is
- Pagina wordt automatisch herladen met PostgreSQL backend
Het dashboard biedt een Backend Switcher component in de header:
- πΎ Mock - TypeScript in-memory database (geen server vereist)
- β‘ Erlang - Echte Erlang/OTP backend (localhost:8080 vereist)
- π PostgreSQL - PostgreSQL database met modern features (localhost:8081 vereist)
De status indicator toont:
- π’ Groen - Backend beschikbaar
- π΄ Rood - Backend offline
- βͺ Grijs - Status onbekend/checking
De backend keuze wordt opgeslagen in localStorage.
npm run buildDe production build wordt geplaatst in de dist/ directory.
Build output:
dist/index.html- Entry pointdist/assets/index-*.css- Compiled styles (~7 KB)dist/assets/index-*.js- Compiled JavaScript (~568 KB)
npm run previewDe app wordt automatisch gedeployed naar GitHub Pages bij elke push naar de master branch via GitHub Actions.
npm run deployDit commando:
- Bouwt de productie versie (
npm run build) - Deployt naar de gh-pages branch
- Publiceert naar GitHub Pages
In de repository settings:
- Ga naar Settings β Pages
- Selecteer Deploy from a branch
- Branch: gh-pages
- Folder: / (root)
Het project gebruikt GitHub Actions voor automatische deployment (.github/workflows/deploy.yml):
name: Deploy to GitHub Pages
on:
push:
branches: [ master ]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '20'
- run: npm ci
- run: npm run build
- uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./distKlik op de "Start Simulatie" knop in de rechterbovenhoek om:
- Automatische status updates te activeren
- Documents door de workflow te laten bewegen
- Live events te genereren in de activity feed
De simulatie update interval is 2 seconden voor soepele progressie.
Simulatie gedrag:
- Selecteert willekeurig een document dat nog niet "Gepubliceerd" is
- Verplaatst het naar de volgende status in de workflow
- Als alle documenten "Gepubliceerd" zijn: reset 1 willekeurig document naar "Ontvangen"
- Broadcast event naar Activity Feed subscribers
1. Statistieken Cards (boven)
- π Totaal Verzoeken: Totaal aantal documenten in database (24)
- π₯ Ontvangen: Aantal documenten in "Ontvangen" status
- β³ In Behandeling: Som van "In behandeling", "1e Concept", "2e Concept", "Definitief"
- β Afgerond: Aantal documenten in "Gepubliceerd" status
2. Grafieken (midden)
-
Verzoeken per Maand (Bar Chart)
- Mock data voor maandelijkse trend
- X-as: Maanden (Jan - Okt)
- Y-as: Aantal verzoeken
-
Status Verdeling (Pie Chart)
- Toont alle 6 workflow statussen
- Kleurgecodeerd per status type
- Percentages berekend real-time
- Label format: "Status: XX%"
3. Documenten Tabel (links beneden)
Kolommen:
- ID: Document identifier (bijv. WOO-UTR-2024-001)
- Titel: Document titel
- Organisatie: ποΈ Gemeente Utrecht / πΊοΈ Provincie Flevoland
- Categorie: Document categorie
- Status: Kleurgecodeerde badge met huidige status
- Ingediend: Indiening datum
- Behandelaar: Afdeling/persoon
4. Activity Feed (rechts beneden)
- Real-time event notificaties via Erlang actor subscription
- Toont laatste 10 events
- Event types:
- Status wijzigingen met oude β nieuwe status
- Kleuren matchen status badges
- Organisatie icon (ποΈ / πΊοΈ)
- Automatische scroll naar nieuwe events
{
"dev": "vite", // Start development server
"build": "tsc && vite build", // TypeScript compile + Vite build
"preview": "vite preview", // Preview productie build lokaal
"predeploy": "npm run build", // Pre-deployment hook (automatic)
"deploy": "gh-pages -d dist" // Deploy dist/ naar gh-pages branch
}Het project gebruikt strikte TypeScript types (src/types.ts):
// Status types met 6 workflow stages
export type WOOStatus =
| "Ontvangen"
| "In behandeling"
| "1e Concept"
| "2e Concept"
| "Definitief"
| "Gepubliceerd";
// Organisatie types
export type OrganizationType = "gemeente" | "provincie";
// Hoofd document interface
export interface WOORequest {
id: string;
title: string;
status: WOOStatus;
submittedDate: string;
organization: string;
organizationType: OrganizationType;
category: string;
subject: string;
requester: string;
handler: string;
decidedDate?: string;
lastModified: string;
}
// Statistieken interface
export interface WOOStats {
totalRequests: number;
received: number;
inProgress: number;
completed: number;
averageHandlingDays: number;
}
// Event interface voor activity feed
export interface Event {
id: string;
timestamp: Date;
type: string;
documentId: string;
message: string;
oldStatus?: WOOStatus;
newStatus?: WOOStatus;
organization: string;
organizationType: OrganizationType;
}
// Chart data interfaces
export interface MonthlyData {
month: string;
requests: number;
}
export interface StatusDistribution {
name: string;
value: number;
color: string;
}
// Organisatie interface
export interface Organization {
id: string;
name: string;
type: OrganizationType;
statusWorkflow: WOOStatus[];
}Status badge kleuren (src/App.css):
/* Ontvangen - Oranje */
.status-received {
background-color: #fef3c7;
color: #d97706;
}
/* In behandeling - Blauw */
.status-in-progress {
background-color: #dbeafe;
color: #107abe;
}
/* 1e Concept - Paars */
.status-concept-1 {
background-color: #f3e8ff;
color: #9333ea;
}
/* 2e Concept - Roze */
.status-concept-2 {
background-color: #fce7f3;
color: #db2777;
}
/* Definitief - Cyaan */
.status-definitive {
background-color: #cffafe;
color: #0891b2;
}
/* Gepubliceerd - Groen */
.status-published {
background-color: #dcfce7;
color: #16a34a;
}- Chrome/Edge (laatste 2 versies)
- Firefox (laatste 2 versies)
- Safari (laatste 2 versies)
Vereist:
- ES2020+ support
- JavaScript enabled
- localStorage (voor potentiΓ«le toekomstige features)
Contributions zijn welkom! Volg deze stappen:
- Fork de repository
- Maak een feature branch (
git checkout -b feature/nieuwe-functie) - Commit je wijzigingen (
git commit -m 'Voeg nieuwe functie toe') - Push naar de branch (
git push origin feature/nieuwe-functie) - Open een Pull Request
- Gebruik TypeScript voor alle nieuwe code
- Volg de bestaande code style
- Test lokaal met
npm run devvoordat je pushed - Build succesvol met
npm run build - Documenteer nieuwe features in README.md
- Update types.ts bij nieuwe interfaces
Dit project is open source en beschikbaar onder de MIT License.
Voor vragen of suggesties, open een issue op GitHub.
Production build:
- HTML: ~0.86 KB (gzip: 0.42 KB)
- CSS: ~7.08 KB (gzip: 2.06 KB)
- JavaScript: ~568 KB (gzip: 161 KB)
Total: ~576 KB (~164 KB gzipped)
- First Contentful Paint: < 1s
- Time to Interactive: < 2s
- Lighthouse Score: 90+
// vite.config.ts
export default defineConfig({
plugins: [react()],
base: '/woo-dashboard/', // GitHub Pages subdirectory
})Het WOO Dashboard is uitgebreid met volledige Document Management Systems voor archivering en beheer van WOO documenten:
1. Paperless-ngx (Lichtgewicht - Aanbevolen)
- Open-source document management voor kleine tot middelgrote organisaties
- PostgreSQL 18 database met MinIO metadata tracking
- Automatische OCR en tagging
- MinIO S3-compatible storage (lokaal, geen AWS cloud!)
- Web UI op http://localhost:8000
2. Alfresco Community Edition (Enterprise)
- Enterprise-grade ECM/DMS platform voor grote organisaties
- Volledige workflow management en compliance features
- Digital Workspace en Share UI
- Vereist 4GB+ RAM, ~10 minuten startup tijd
- Web UI op http://localhost:8080
Interactieve component in het dashboard voor het uploaden van test documenten:
Features:
- π Variabel aantal documenten (1-50) via slider
- π Realistische PDF generatie met PDFKit
- ποΈ 6 documenttypes: besluit, advies, brief, notitie, rapportage, contract
- π Real-time progress tracking
- π― Upload naar Paperless, Alfresco, of beide systemen
Gebruik:
- Start het DMS systeem (zie hieronder)
- Open dashboard op http://localhost:5173
- Scroll naar "Document Management Simulator"
- Stel aantal documenten in
- Selecteer doelsysteem(en)
- Klik "Start Simulatie"
Alle document uploads worden geregistreerd in een NATS JetStream event systeem:
Features:
- π‘ Persistent event storage (7 dagen retentie)
- π Complete audit trail van alle document operaties
- π Real-time event stream viewer in dashboard
- π― Event types: document.uploaded, document.updated, document.deleted
- π Statistieken: totaal events, per systeem, gemiddelde upload tijd
Event Schema:
{
eventId: "evt-1234-xyz",
eventType: "document.uploaded",
timestamp: "2024-11-09T10:30:00Z",
system: "paperless",
document: {
id: "doc-123",
title: "Besluit gemeenteraad 2024-01",
type: "besluit",
category: "Bestuur",
size: 45678
},
metadata: {
simulationId: "sim-456",
uploadDuration: 1234,
source: "dms-simulator"
}
}Beide DMS systemen gebruiken MinIO voor object storage met volledige metadata tracking in PostgreSQL:
Features:
- πͺ£ Automatische bucket statistieken
- π¦ Object metadata tracking (size, tags, upload time)
- π SQL views voor analytics (v_bucket_overview, v_recent_uploads)
- π Query capabilities via PostgreSQL
Voorbeeld Query:
SELECT * FROM minio_metadata.v_bucket_overview;
SELECT * FROM minio_metadata.v_recent_uploads LIMIT 10;Volledig systeem starten:
./start-all.shDit script start in volgorde:
- NATS JetStream (event streaming)
- DMS keuze (Paperless, Alfresco, of beide)
- DMS Simulator (document upload service)
- WOO Dashboard (frontend)
Alleen NATS + Paperless:
# Start NATS
cd nats-events && docker-compose up -d && cd ..
# Start Paperless
cd paperless-ngx-dms && docker-compose up -d && cd ..
# Start DMS Simulator
cd dms-simulator && npm run dev &
# Start Dashboard
npm run devVerificatie:
./verify-system.shControleert alle services en endpoints.
Dashboard β DMS Simulator β [Paperless/Alfresco] β MinIO
β
NATS JetStream β Event Consumer API
β
Event Stream Viewer (Dashboard Component)
Uitgebreide documentatie per component:
COMPLETE_SYSTEM_GUIDE.md- Volledige systeemgids met alle featurespaperless-ngx-dms/README.md- Paperless-ngx setup en configuratiealfresco-dms/README.md- Alfresco setup en vergelijkingnats-events/README.md- NATS JetStream architectuurnats-events/QUICKSTART.md- NATS snelstart gidsdms-simulator/README.md- Document generator details
π Major New Features:
- π Paperless-ngx DMS - Lichtgewicht document management met MinIO + PostgreSQL 18
- π’ Alfresco DMS - Enterprise ECM platform voor grote organisaties
- π DMS Simulator - Interactieve document generator met realistische PDFs
- π‘ NATS JetStream - Event streaming platform voor complete audit trail
- π Event Stream Viewer - Real-time dashboard component voor events
- ποΈ MinIO Metadata Tracking - PostgreSQL schema voor object storage analytics
DMS Features:
- β¨ Twee volledige DMS opties (Paperless-ngx & Alfresco)
- π Realistische PDF generatie (6 documenttypes)
- π Real-time upload progress tracking
- π― Multi-systeem support (upload naar beide DMS tegelijk)
- π‘ NATS event publishing bij elke upload
- ποΈ MinIO S3-compatible lokale storage (geen AWS!)
- π PostgreSQL 18 met metadata tracking
Event System Features:
- π― NATS JetStream met 7-dagen retentie
- π‘ Server-Sent Events voor live updates
- π Statistieken dashboard (totaal, per systeem, gemiddelde tijd)
- π Event filtering en querying via REST API
- πΎ Persistent event storage met replay capabilities
Developer Tools:
- π
start-all.sh- Complete systeem startup script - π
verify-system.sh- Service verification tool - π
COMPLETE_SYSTEM_GUIDE.md- Uitgebreide documentatie - π³ Docker Compose setups voor alle componenten
π Major New Feature:
- β‘ Echte Erlang/OTP Backend - Volledige OTP applicatie met gen_server, supervisor, gen_event
- π Dual Backend Architecture - Switch tussen Mock en Erlang backend via UI
- π REST API - Complete RESTful API met Cowboy web server
- ποΈ ETS Database - Production-grade Erlang Term Storage
- πΎ Backend Switcher Component - Real-time backend switching met status indicator
Erlang Backend Features:
- Gen_Server voor document management (woo_document_manager)
- Gen_Event voor event notifications (woo_event_manager)
- Gen_Server voor simulation (woo_simulation_server)
- Supervisor tree met one_for_one restart strategy
- CORS-enabled REST API (port 8080)
- Health check endpoint
- 8 initial documents (4 Utrecht, 4 Flevoland)
Frontend Improvements:
- π
erlangBackendService.ts- Backend API client - ποΈ
backendService.ts- Abstraction layer voor backend switching - π¨
BackendSwitchercomponent met status indicator - π¦ localStorage persistence voor backend keuze
- β¨ v2.1 badge met "Multi-Backend" label
Documentation:
- π Complete Erlang backend README (erlang-backend/README.md)
- π§ Setup instructies voor beide backends
- π API documentatie met curl voorbeelden
- ποΈ OTP architectuur uitleg
- π‘ Frontend integration examples
Breaking Changes:
- Verwijderd SQL.js dependency (CORS/WASM issues)
- Vervangen door pure JavaScript mock database
New Features:
- β¨ Mock database met 24 realistische documenten
- β¨ Detailed pie chart met alle 6 workflow statussen
- β¨ Cyclische status progressie (auto-restart)
- β¨ 2-seconden simulatie interval
- β¨ Verbeterde kleuren voor alle statussen
Improvements:
- π¨ Unieke kleuren voor elk status type in pie chart
- π Betere data distributie (4 docs per status)
- π Snellere load time (geen WASM)
- πΎ Kleinere bundle size (562 KB vs 612 KB)
- π§ Simpelere maintenance (geen externe DB)
Bug Fixes:
- π Fixed GitHub Pages deployment CORS issues
- π Fixed status progression getting stuck
- π Fixed pie chart not showing all statuses
- InitiΓ«le release met SQL.js database
- Erlang Actor System implementatie (TypeScript simulatie)
- GitHub Pages deployment
- Basis simulatie systeem
Gebouwd met β€οΈ voor transparantie in de Nederlandse overheid
Tech Stack: React 18 β’ TypeScript β’ Vite β’ Erlang/OTP Patterns β’ Recharts