diff --git a/CHANGELOG.md b/CHANGELOG.md
index 30e1e2e..ca510c1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,7 +14,8 @@ Alle betekenisvolle wijzigingen worden hier bijgehouden. Het project volgt voorl
### Gewijzigd
- De repository-README volgt nu de gebruikersreis van doel en dagelijkse workflow naar quickstart, configuratie, veiligheidsgrenzen, beheer en pas daarna architectuur en agentoverdracht.
-- Vier actuele, compacte productbeelden documenteren dagoverzicht, vacatureverkenner, automatisering en het mobiele zoekprofiel in dark/light zonder externe runtime-assets.
+- Negen actuele, compacte productbeelden vertellen nu de volledige gebruikersreis: dagoverzicht, vacatureverkenner, beslisanalyse, Skillsradar, werkgevers, sollicitaties, bronnen, automatisering en mobiel zoekprofiel.
+- De README-galerij gebruikt taakgerichte captions, klikbare beelden, een consistente 1920×1080-desktopviewport en een gebalanceerde dark/light-compositie.
- Het VacatureRadar-productlogo is hertekend als transparante vier-elementenradar zonder donkere tegel, rand of schaduw; login en sidebar gebruiken hetzelfde cleaner beeldmerk.
- De sidebarfooter combineert radarstatus en een transparante, themabewuste ITWorx-credit; de dubbele demo-/accountidentiteit is verwijderd en blijft uitsluitend via het topbarmenu bereikbaar.
- Widescreencontent schaalt nu gecontroleerd naar 2.200 px en op 4K naar 2.480 px, terwijl dashboardverhoudingen, korte viewports en mobiele layouts begrensd blijven.
@@ -32,6 +33,7 @@ Alle betekenisvolle wijzigingen worden hier bijgehouden. Het project volgt voorl
### Hersteld
+- De productieconfiguratiehelper voldoet opnieuw aan Ruff en de `.env`-bestandmodustest houdt rekening met Windows, waar POSIX-modusbits niet afdwingbaar zijn.
- Publieke Nginx-hostnamen veroorzaken niet langer `DisallowedHost` wanneer `PUBLIC_BASE_URL` correct staat.
- HTTPS-deployment gebruikt de externe poort in plaats van de interne applicatiepoort voor CSRF en publieke URL’s.
- Interne healthchecks blijven bereikbaar bij `SECURE_SSL_REDIRECT=1`; productie-rate-limiting gebruikt een gedeelde Redis-cache.
diff --git a/README.md b/README.md
index 4c4fb54..d893058 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,10 @@
-# VacatureRadar
+
+
+
-**Van vacature-overload naar een kleine, uitlegbare selectie die bij jou past.**
+VacatureRadar
+
+Van vacature-overload naar een kleine, uitlegbare selectie die bij jou past.
VacatureRadar is een persoonlijke vacature-assistent voor België. De applicatie volgt toegestane werkgeversbronnen, ATS-pagina’s, feeds en vacaturemails, verwijdert dubbels en irrelevante resultaten en rangschikt de overblijvende kansen tegen jouw zoekprofiel. Jij behoudt altijd de controle: VacatureRadar helpt zoeken en opvolgen, maar solliciteert nooit automatisch.
@@ -36,18 +40,65 @@ VacatureRadar is bedoeld voor één persoon of een eigen, afgeschermde installat
| Bronnenbeheer | Veilige bronconfiguratie, gezondheid, handmatige import en mailboxkoppelingen |
| Automatisering | Eerlijke 24-uursmetingen; niet-gemeten informatie blijft expliciet onbekend |
-## Productbeelden
+## Bekijk de volledige productflow
-De beelden hieronder komen uit de lokale demo-omgeving. De getoonde cijfers zijn omgevingsdata, geen marketingclaims.
+De beelden komen uit de lokale demo-omgeving. De getoonde cijfers zijn omgevingsdata, geen marketingclaims. Klik op een beeld voor de volledige resolutie.
-| Vacatureverkenner | Automatisering |
-|---|---|
-|  |  |
+### 1. Vind de relevante kansen
+
+De verkenner brengt zoeken, filters, gerangschikte resultaten en de belangrijkste matchcontext samen. Zo hoef je niet tussen lijst- en detailpagina’s te blijven wisselen.
-
+
+### 2. Begrijp waarom een vacature past
+
+Een analyse maakt score, datakwaliteit, beslissignalen, aandachtspunten en broninformatie afzonderlijk zichtbaar. Ervaring en senioriteitslabels worden als context getoond, niet als verborgen scorefactor.
+
+
+
+
+
+### 3. Bouw markt- en werkgeversinzicht op
+
+
+
+ Skillsradar Vergelijk aantoonbare marktvraag met je profiel, zonder ontbrekende skills als afwijzingsregel te behandelen. |
+ Werkgeversradar Bekijk werkgevers, actieve IT-vacatures, bronnen en eerdere dossiers in één genormaliseerd overzicht. |
+
+
+  |
+  |
+
+
+
+### 4. Volg trajecten op en houd bronnen gezond
+
+
+
+ Sollicitatiepipeline Bewaar status, deadlines, notities en tijdlijn per dossier; de uiteindelijke sollicitatie blijft altijd handmatig. |
+ Bronnenbeheer Zie policyreview, laatste scans, volgende runs en brongezondheid zonder technische logs te moeten doorzoeken. |
+
+
+  |
+  |
+
+
+
+### 5. Controleer de radar — ook mobiel
+
+
+
+ Automation center Volg de echte verwerkingspipeline, bronruns en systeemstatus. Niet-gemeten runtime-informatie blijft expliciet onbekend. |
+ Mobiel zoekprofiel Controleer je persoonlijke radar en navigeer door de kernflow vanaf een smal scherm. |
+
+
+  |
+  |
+
+
+
## Snel starten
### Optie 1 — lokale demo
diff --git a/docs/ai/BACKLOG.yaml b/docs/ai/BACKLOG.yaml
index a7dbbf3..eab3eac 100644
--- a/docs/ai/BACKLOG.yaml
+++ b/docs/ai/BACKLOG.yaml
@@ -1,6 +1,6 @@
schema_version: 1
project: VacatureRadar
-updated_at: '2026-07-27'
+updated_at: '2026-07-28'
execution_policy:
order: file-order, then first ready task whose dependencies are done
no_product_questions: true
@@ -2215,13 +2215,14 @@ tasks:
summary: Maak de bestaande persoonlijke installatie veilig en reproduceerbaar bereikbaar achter Nginx Proxy Manager,
herstel publieke host- en CSRF-configuratie en voorkom dat een gedeeld demoaccount persistente data wijzigt.
acceptance_criteria:
- - Eén publieke HTTPS-URL configureert hostvalidatie, CSRF, secure cookies, SSL-detectie en redirect zonder de interne
- containerpoort in de externe origin op te nemen.
+ - Eén publieke HTTPS-URL configureert hostvalidatie, CSRF, secure cookies, SSL-detectie en redirect zonder de
+ interne containerpoort in de externe origin op te nemen.
- Productie start fail-fast bij debugmodusgerelateerde onveiligheid, een zwakke secret, onveilige cookies, ontbrekende
HTTPS-origin of een schrijfbare publieke demo.
- Login- en importlimieten gebruiken in productie een gedeelde cache; forwarded client-IP’s worden alleen vanaf
expliciet vertrouwde proxy-CIDR’s geaccepteerd.
- - Het gastaccount is server-side alleen-lezen, heeft een begrensde sessie, kan afmelden en toont de beperking zichtbaar.
+ - Het gastaccount is server-side alleen-lezen, heeft een begrensde sessie, kan afmelden en toont de beperking
+ zichtbaar.
- Unraid-envpad, Nginx Proxy Manager, healthchecks, noindex, heruitrol en terugdraaien zijn exact gedocumenteerd.
verification:
- PYTHONPATH=/mnt/data/vr_windows_site/site-packages pytest -q
@@ -2244,3 +2245,41 @@ tasks:
note: Publieke host/CSRF/HTTPS-configuratie, gedeelde Redis-rate-limiting, proxy-IPvertrouwen, cache-readiness,
noindex en read-only gastdemo geïmplementeerd. 295 tests groen met 2 correcte Playwrightskips; Django check,
migratiecheck, repositoryvalidatie, YAML-, syntax- en packagechecks groen. Live servervalidatie blijft extern.
+- id: VR-226
+ title: Verdiep de user-first README met een volledige productgalerij
+ status: done
+ priority: P1
+ requirement_ids:
+ - NFR-006
+ - NFR-008
+ - NFR-009
+ depends_on:
+ - VR-225
+ summary: Maak de productpresentatie overtuigender met een logisch opgebouwde, actuele galerij die de volledige
+ gebruikersreis toont zonder de README traag, repetitief of marketingmatig te maken.
+ acceptance_criteria:
+ - De README bevat minstens acht actuele beelden die dashboard, zoeken/analyse, skills, sollicitatieopvolging,
+ profiel, bronnen, automatisering en mobiel gebruik als één samenhangende productreis tonen.
+ - Desktopbeelden gebruiken een consistente professionele viewport en rustige uitsnede; dark/light en mobiel zijn
+ doelbewust verdeeld, zonder overgangsoverlays, overflow, consolefouten, secrets of persoonlijke gegevens.
+ - De galerij heeft korte taakgerichte captions, een scanbare compositie en compacte lokale assets; duplicatieve
+ beelden of losse decoratieve screenshots worden verwijderd of vervangen.
+ - De eerder aangetroffen VR-225-baselinefouten in lint en platformafhankelijke bestandsmoduscontrole zijn hersteld;
+ repositoryvalidatie en de volledige kwaliteitsgate slagen.
+ verification:
+ - Browsercontrole van alle vastgelegde routes op desktop en mobiel in dark/light
+ - uv run pytest tests/unit/test_public_configuration.py
+ - python scripts/validate_repository.py
+ - ./scripts/codex_verify.sh
+ primary_paths:
+ - README.md
+ - docs/screenshots/readme/
+ - scripts/configure_public_url.py
+ - tests/unit/test_public_configuration.py
+ - docs/quality/TRACEABILITY_MATRIX.md
+ - docs/ai/PROJECT_STATE.md
+ result:
+ completed_at: '2026-07-28'
+ note: README verdiept tot een klikbare user-first productreis met negen actuele beelden op 1920x1080 en 390x844
+ in dark/light; alle routes zonder overflow of consolewaarschuwingen. VR-225-lint en Windows-modusassertie
+ hersteld; 296 tests groen, 2 skips en 83,44% dekking.
diff --git a/docs/ai/PROJECT_STATE.md b/docs/ai/PROJECT_STATE.md
index 508b6a3..70b1951 100644
--- a/docs/ai/PROJECT_STATE.md
+++ b/docs/ai/PROJECT_STATE.md
@@ -1,6 +1,6 @@
# Projectstatus
-- Laatst bijgewerkt: 2026-07-27
+- Laatst bijgewerkt: 2026-07-28
- Repositoryversie: 0.3.12 productie- en reverse-proxyhardening
- Uitvoeringsmodus: autonome backlog
- Eerste uitvoerbare taak: controleer met `python scripts/backlog.py next`
@@ -46,6 +46,13 @@ Het volledige scherm- en afwijkingsrapport staat in `docs/design/STITCH_INTEGRAT
## Laatste onderhoudsaudit
+VR-226 op 2026-07-28:
+
+- README-presentatie verdiept van vier losse beelden naar een klikbare productreis met negen actuele screenshots: overzicht, zoeken, analyse, skills, werkgevers, sollicitaties, bronnen, automatisering en mobiel profiel;
+- alle desktopbeelden opnieuw vastgelegd op 1920×1080, doelbewust verdeeld over dark/light en gecombineerd met 390×844 mobiel; geen overgangsoverlays, horizontale overflow, secrets of consolewaarschuwingen;
+- de onrustige mobiele dashboardcapture is afgekeurd en verwijderd; de volledige galerij blijft lokaal en samen circa 1,1 MB;
+- vier aangetroffen VR-225-lintproblemen en de ongeldige POSIX-modusassertie op Windows zijn hersteld; volledige gate groen met 296 geslaagde tests, 2 optionele skips en 83,44% branch-aware dekking. Volgende uitvoerbare taak: geen.
+
VR-224 op 2026-07-27:
- README volledig herordend rond doelgroep, dagelijkse workflow, concrete productwaarde, veilige quickstart en eerste configuratie; architectuur, beheer en Codex-overdracht volgen pas daarna;
diff --git a/docs/quality/TRACEABILITY_MATRIX.md b/docs/quality/TRACEABILITY_MATRIX.md
index 8c21c8e..e254a22 100644
--- a/docs/quality/TRACEABILITY_MATRIX.md
+++ b/docs/quality/TRACEABILITY_MATRIX.md
@@ -61,9 +61,9 @@ Legenda: **I** = geïmplementeerd en getest in de huidige MVP; **P** = gedeeltel
| NFR-003 | I/P | replay-, mail- en outboxtests; per-bronfouten | VR-103/112 |
| NFR-004 | I | ScoreRun, provenance, skillvraag met expliciet veld-/tekstonderscheid en UI-copy | VR-109/208 uitgebreid evidence |
| NFR-005 | I | domeinapps/services/adapters/ADRs | continue review |
-| NFR-006 | I | offline pytest + coveragegate | VR-114 verbreedt e2e |
+| NFR-006 | I | offline pytest + coveragegate, inclusief platformbewuste productieconfiguratietests | VR-114 e2e; `tests/unit/test_public_configuration.py`; VR-225/226 |
| NFR-007 | P | `scripts/benchmark.py`, `tests/benchmark/test_benchmark.py` | VR-116 |
-| NFR-008 | I | volledige negen-schermen-Stitch-cockpit, acht viewports, dark/light, toetsenborddrawer, premium visuele hiërarchie, zelfstandig VacatureRadar-productmerk, lokale ITWorx-ontwikkelaarscredit, toegankelijke interactieve radarillustratie en actuele user-first productdocumentatie | `tests/e2e/test_vr114_browser_and_accessibility.py`, `tests/integration/test_intelligence_views.py`, `tests/integration/test_views.py`, `artifacts/visual-validation/`, `docs/screenshots/readme/`, VR-209–224 |
+| NFR-008 | I | volledige negen-schermen-Stitch-cockpit, acht viewports, dark/light, toetsenborddrawer, premium visuele hiërarchie, zelfstandig VacatureRadar-productmerk, lokale ITWorx-ontwikkelaarscredit, toegankelijke interactieve radarillustratie en actuele user-first productdocumentatie | `tests/e2e/test_vr114_browser_and_accessibility.py`, `tests/integration/test_intelligence_views.py`, `tests/integration/test_views.py`, `artifacts/visual-validation/`, negen beelden in `docs/screenshots/readme/`, VR-209–224/226 |
| NFR-009 | I | lokale SQLite, configureerbare eigenaarbranding, Docker/Unraidconfig, publieke URL-helper, Nginx Proxy Manager-runbook, gevalideerde SSH-deployalias, release-smoke en restore | `tests/unit/test_deployment_configuration.py`, `tests/unit/test_public_configuration.py`, VR-117, VR-217/218/225 afgerond |
| NFR-010 | I | `apps/sources/services/health.py`, `apps/sources/tasks.py`, `apps/core/views.py`, `config/settings.py` | `tests/integration/test_source_health.py` | VR-112 observability |
@@ -113,3 +113,4 @@ Iedere backlogtaak die een requirement wijzigt, werkt in dezelfde commit deze ma
- VR-224 maakt de repository-ingang user-first: de README volgt de dagelijkse productflow, bevat gecontroleerde lokale quickstarts en gebruikt vier actuele dark/light- en desktop/mobielscreenshots zonder secrets of externe assets.
- VR-225 hardent de publieke installatie met `PUBLIC_BASE_URL`, exacte host/CSRF-afleiding, veilige proxy-HTTPS-detectie, vertrouwde client-IPchains, gedeelde Redis-rate-limiting, cache-readiness en een server-side alleen-lezen gastdemo. Bewijs staat in de productie-audit, Nginx-runbook en de settings-, network-, health-, auth- en deploymenttests.
+- VR-226 verdiept de README tot een visuele productreis met negen actuele beelden op consistente desktop- en mobiele viewports. Dezelfde taak herstelt de VR-225-lintbaseline en maakt de POSIX-bestandsmodusassertie terecht platformafhankelijk.
diff --git a/docs/screenshots/readme/applications-dark.png b/docs/screenshots/readme/applications-dark.png
new file mode 100644
index 0000000..179902e
Binary files /dev/null and b/docs/screenshots/readme/applications-dark.png differ
diff --git a/docs/screenshots/readme/automation-dark.png b/docs/screenshots/readme/automation-dark.png
index 6c31edd..ab54e34 100644
Binary files a/docs/screenshots/readme/automation-dark.png and b/docs/screenshots/readme/automation-dark.png differ
diff --git a/docs/screenshots/readme/dashboard-light.png b/docs/screenshots/readme/dashboard-light.png
index bf7903b..d475e22 100644
Binary files a/docs/screenshots/readme/dashboard-light.png and b/docs/screenshots/readme/dashboard-light.png differ
diff --git a/docs/screenshots/readme/employers-light.png b/docs/screenshots/readme/employers-light.png
new file mode 100644
index 0000000..e8e137f
Binary files /dev/null and b/docs/screenshots/readme/employers-light.png differ
diff --git a/docs/screenshots/readme/job-analysis-dark.png b/docs/screenshots/readme/job-analysis-dark.png
new file mode 100644
index 0000000..0a0492d
Binary files /dev/null and b/docs/screenshots/readme/job-analysis-dark.png differ
diff --git a/docs/screenshots/readme/matches-dark.png b/docs/screenshots/readme/matches-dark.png
index e461dea..661eef8 100644
Binary files a/docs/screenshots/readme/matches-dark.png and b/docs/screenshots/readme/matches-dark.png differ
diff --git a/docs/screenshots/readme/profile-mobile-light.png b/docs/screenshots/readme/profile-mobile-light.png
index 7970039..af2e97c 100644
Binary files a/docs/screenshots/readme/profile-mobile-light.png and b/docs/screenshots/readme/profile-mobile-light.png differ
diff --git a/docs/screenshots/readme/skills-light.png b/docs/screenshots/readme/skills-light.png
new file mode 100644
index 0000000..008e1d0
Binary files /dev/null and b/docs/screenshots/readme/skills-light.png differ
diff --git a/docs/screenshots/readme/sources-light.png b/docs/screenshots/readme/sources-light.png
new file mode 100644
index 0000000..4dddb85
Binary files /dev/null and b/docs/screenshots/readme/sources-light.png differ
diff --git a/scripts/configure_public_url.py b/scripts/configure_public_url.py
index 31b7441..b221826 100644
--- a/scripts/configure_public_url.py
+++ b/scripts/configure_public_url.py
@@ -16,11 +16,17 @@ def normalize_public_url(value: str) -> tuple[str, str]:
raw = value.strip().rstrip("/")
parsed = urlparse(raw)
if parsed.scheme not in {"http", "https"} or not parsed.hostname:
- raise ConfigurationError("Gebruik een volledige http(s)-URL, bijvoorbeeld https://jobs.example.be")
+ raise ConfigurationError(
+ "Gebruik een volledige http(s)-URL, bijvoorbeeld https://jobs.example.be"
+ )
if parsed.username or parsed.password or parsed.query or parsed.fragment:
- raise ConfigurationError("De publieke URL mag geen credentials, query of fragment bevatten.")
+ raise ConfigurationError(
+ "De publieke URL mag geen credentials, query of fragment bevatten."
+ )
if parsed.path not in {"", "/"}:
- raise ConfigurationError("De applicatie moet op de root van het domein gepubliceerd worden.")
+ raise ConfigurationError(
+ "De applicatie moet op de root van het domein gepubliceerd worden."
+ )
try:
port = parsed.port
except ValueError as exc:
diff --git a/tests/unit/test_public_configuration.py b/tests/unit/test_public_configuration.py
index 2ae6380..6475fca 100644
--- a/tests/unit/test_public_configuration.py
+++ b/tests/unit/test_public_configuration.py
@@ -43,6 +43,7 @@ def test_public_url_rejects_unsafe_or_unsupported_values(raw):
with pytest.raises((ImproperlyConfigured, ConfigurationError)):
normalize_public_base_url(raw)
+
def test_configure_public_url_updates_only_public_security_settings(tmp_path: Path):
env_file = tmp_path / ".env"
env_file.write_text(
@@ -66,20 +67,22 @@ def test_configure_public_url_updates_only_public_security_settings(tmp_path: Pa
assert "DJANGO_DEBUG=0" in content
assert "vacatureradar.itworx.tech" in content
assert (
- "DJANGO_CSRF_TRUSTED_ORIGINS="
- "http://192.168.10.150:1226,https://vacatureradar.itworx.tech"
+ "DJANGO_CSRF_TRUSTED_ORIGINS=http://192.168.10.150:1226,https://vacatureradar.itworx.tech"
) in content
assert "CACHE_URL=redis://127.0.0.1:6379/1" in content
assert "TRUSTED_PROXY_CIDRS=172.18.0.0/16" in content
assert "DJANGO_SECRET_KEY=keep-this-secret" in content
assert "POSTGRES_PASSWORD=keep-this-password" in content
- assert env_file.stat().st_mode & 0o777 == 0o600
+ if os.name != "nt":
+ assert env_file.stat().st_mode & 0o777 == 0o600
assert "DJANGO_SECRET_KEY" not in changed
+
def test_configure_public_url_rejects_plain_http(tmp_path: Path):
with pytest.raises(ConfigurationError, match="HTTPS"):
configure_env(tmp_path / ".env", "http://vacatureradar.example.be")
+
def test_public_base_url_populates_django_host_and_csrf_settings():
environment = os.environ.copy()
environment.update(
@@ -91,7 +94,7 @@ def test_public_base_url_populates_django_host_and_csrf_settings():
"SECURE_HSTS_SECONDS": "300",
}
)
- result = subprocess.run(
+ result = subprocess.run( # noqa: S603 - fixed interpreter and static assertion program
[
sys.executable,
"-c",
@@ -111,9 +114,10 @@ def test_public_base_url_populates_django_host_and_csrf_settings():
capture_output=True,
text=True,
check=False,
- ) # noqa: S603 - fixed interpreter and static assertion program
+ )
assert result.returncode == 0, result.stderr
+
def test_configure_public_url_is_idempotent(tmp_path: Path):
env_file = tmp_path / ".env"
env_file.write_text("DJANGO_ALLOWED_HOSTS=localhost\n", encoding="utf-8")