diff --git a/CHANGELOG.md b/CHANGELOG.md index f5ba75d..9ead33c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,8 +6,10 @@ Alle betekenisvolle wijzigingen worden hier bijgehouden. Het project volgt voorl ### Gewijzigd -- De ultrawide cockpit gebruikt één links verankerd grid tot 2800 px; headers, filters, pipeline, KPI's en detailpanelen delen daardoor op brede schermen hetzelfde startpunt zonder toevallige lege linkerkolom. -- De officiële ITWorx.tech-wordmark en het cloud-check-symbool uit het door de eigenaar aangeleverde asset pack vormen nu de merklaag van sidebar, bovenbalk, login, favicon en Dockermanicoon; VacatureRadar blijft zichtbaar als productnaam. +- De volledige visuele audit is vertaald naar één gedeeld contentframe voor topbar en pagina, een dashboard met maximaal vijf kernmatches en een compactere activiteitstijdlijn. +- Secundaire actielinks hebben grotere klikdoelen, reduced motion dekt alle animaties en geïmporteerde stylesheets delen voortaan expliciet de releasecacheversie. +- De ultrawide cockpit gebruikt één begrensd, links verankerd grid; headers, filters, pipeline, KPI's en detailpanelen delen daardoor op brede schermen hetzelfde startpunt zonder uitgerekte leesregels. +- VacatureRadar heeft een zelfstandig open-radar-beeldmerk met scanstraal en kanssignaal voor shell, login, favicon en Dockerman; de officiële ITWorx.tech-wordmark staat uitsluitend nog als subtiele `Ontwikkeld door`-credit. - VacatureRadar gebruikt nu één scherper radarbeeldmerk voor favicon, login en applicatieshell; de persoonlijke eigenaar is via `VACATURERADAR_OWNER_NAME` configureerbaar. - De bovenbalk geeft naast zoeken ook eigenaar en actuele werkcontext aan; navigatie-, focus- en hoverstates zijn verfijnd zonder de compacte mobiele cockpit te verzwaren. - Het zoekprofiel bevat een lokale interactieve radarillustratie met pointerparallax, een rustige scansweep, light/dark-kleuren en een statische reduced-motionvariant; de illustratie is decoratief en introduceert geen fictieve data. @@ -18,6 +20,7 @@ Alle betekenisvolle wijzigingen worden hier bijgehouden. Het project volgt voorl ### Hersteld +- Op mobiel overlappen `Wachtwoord` en `Wachtwoord vergeten?` niet meer; loginiconen zijn zelfstandige lijn-SVG's en de dubbele herstel-link is verwijderd. - De Unraid-synchelper negeert verwijderde indexpaden bij een release vanuit een geldige dirty worktree; vervangen merkassets blokkeren de archiefopbouw daardoor niet meer. - Rechtstreekse SSH naar het Unraid-IP koos lokaal de verkeerde gebruiker en identiteit; het gedocumenteerde aliasprofiel selecteert nu reproduceerbaar `root` met de afzonderlijke deploysleutel. - Inloginteracties laden nu als lokale externe JavaScriptasset; de eigen Content Security Policy blokkeert wachtwoordweergave en submitfeedback niet langer. diff --git a/apps/core/auth_backends.py b/apps/core/auth_backends.py index 0bc2e37..89fa2f3 100644 --- a/apps/core/auth_backends.py +++ b/apps/core/auth_backends.py @@ -24,8 +24,7 @@ class EmailOrUsernameModelBackend(ModelBackend): try: user = user_model.objects.get( - Q(**{f"{user_model.USERNAME_FIELD}__iexact": username}) - | Q(email__iexact=username) + Q(**{f"{user_model.USERNAME_FIELD}__iexact": username}) | Q(email__iexact=username) ) except user_model.DoesNotExist: # Draai toch een hash om timing-aanvallen (user-enumeratie) te beperken. @@ -34,9 +33,7 @@ class EmailOrUsernameModelBackend(ModelBackend): except user_model.MultipleObjectsReturned: # Meerdere accounts met dezelfde e-mail: val terug op exacte username-match. user = ( - user_model.objects.filter( - **{f"{user_model.USERNAME_FIELD}__iexact": username} - ) + user_model.objects.filter(**{f"{user_model.USERNAME_FIELD}__iexact": username}) .order_by("id") .first() ) diff --git a/apps/core/context_processors.py b/apps/core/context_processors.py index 16c11de..e7f06c5 100644 --- a/apps/core/context_processors.py +++ b/apps/core/context_processors.py @@ -28,8 +28,7 @@ def navigation_context(request: HttpRequest) -> dict[str, Any]: } context = { "app_name": "VacatureRadar", - "app_version": "0.3.3", - "app_brand_name": "ITWorx.tech", + "app_version": "0.3.9", "app_owner_name": settings.VACATURERADAR_OWNER_NAME, "page_context_label": page_contexts.get(route_key, "Persoonlijke cockpit"), } diff --git a/apps/core/entra.py b/apps/core/entra.py index 6f45da3..94743a3 100644 --- a/apps/core/entra.py +++ b/apps/core/entra.py @@ -45,9 +45,7 @@ def load_config() -> EntraConfig: ) scopes = list(getattr(settings, "ENTRA_SCOPES", ["User.Read"])) allowed = tuple( - d.lower().lstrip("@") - for d in getattr(settings, "ENTRA_ALLOWED_DOMAINS", []) - if d + d.lower().lstrip("@") for d in getattr(settings, "ENTRA_ALLOWED_DOMAINS", []) if d ) return EntraConfig( client_id=client_id, @@ -91,10 +89,7 @@ def extract_identity(token_result: dict) -> tuple[str, str]: """Haal (e-mailadres, weergavenaam) uit de id-token-claims.""" claims = token_result.get("id_token_claims") or {} email = ( - claims.get("preferred_username") - or claims.get("email") - or claims.get("upn") - or "" + claims.get("preferred_username") or claims.get("email") or claims.get("upn") or "" ).strip() name = (claims.get("name") or "").strip() return email, name diff --git a/apps/core/views.py b/apps/core/views.py index 0f7e410..8e0b6f7 100644 --- a/apps/core/views.py +++ b/apps/core/views.py @@ -147,9 +147,7 @@ def entra_login(request): return redirect("login") try: config = entra_sso.load_config() - redirect_uri = config.redirect_uri or request.build_absolute_uri( - reverse("entra-callback") - ) + redirect_uri = config.redirect_uri or request.build_absolute_uri(reverse("entra-callback")) flow = entra_sso.build_auth_flow(config, redirect_uri) except entra_sso.EntraConfigError as exc: logger.warning("Entra ID niet beschikbaar: %s", exc) @@ -183,9 +181,7 @@ def entra_callback(request): return redirect("login") if "error" in result: - logger.warning( - "Entra ID-fout: %s", result.get("error_description") or result.get("error") - ) + logger.warning("Entra ID-fout: %s", result.get("error_description") or result.get("error")) messages.error(request, "Inloggen via Entra ID is mislukt.") return redirect("login") diff --git a/apps/jobs/services/cockpit.py b/apps/jobs/services/cockpit.py index cde2bff..0250391 100644 --- a/apps/jobs/services/cockpit.py +++ b/apps/jobs/services/cockpit.py @@ -137,7 +137,9 @@ def build_dashboard_cockpit(user: User, profile: SearchProfile | None) -> dict[s day_conclusion = "Geen nieuw sterk signaal; je bestaande opvolging blijft prioritair." return { - "score_cards": list(latest_scores.order_by("-score", "-created_at")[:8]), + # Het dagoverzicht blijft een beslissingsscherm; de volledige rangschikking + # hoort in de verkenner. Vijf kaarten houden de primaire taak scanbaar. + "score_cards": list(latest_scores.order_by("-score", "-created_at")[:5]), "priority_applications": list( open_applications.select_related("job", "job__employer").order_by( F("follow_up_date").asc(nulls_last=True), "-updated_at" diff --git a/config/urls.py b/config/urls.py index d749599..3c38df1 100644 --- a/config/urls.py +++ b/config/urls.py @@ -18,7 +18,7 @@ from apps.core.views import ( urlpatterns = [ path( "favicon.ico", - RedirectView.as_view(url=static_url("img/itworx-app-icon.png"), permanent=True), + RedirectView.as_view(url=static_url("favicon.svg"), permanent=True), name="favicon", ), path("admin/", admin.site.urls), diff --git a/docs/ai/BACKLOG.yaml b/docs/ai/BACKLOG.yaml index 5abb9e8..747e5aa 100644 --- a/docs/ai/BACKLOG.yaml +++ b/docs/ai/BACKLOG.yaml @@ -1994,6 +1994,89 @@ tasks: exact afgeleid voor 512px favicon/app-icon en 256px Dockermanicoon. Browsermatrix 390/1280/1920 schoon, 267 tests groen en live image sha256:daf34407813b37cdf2d0fc65a482ded65375f89eedd3f0144a8c0ce206d41565 healthy met readiness/PNG-smokes. +- id: VR-221 + title: Scheid appmerk van ontwikkelaarsbranding + status: done + priority: P1 + requirement_ids: + - NFR-004 + - NFR-008 + - NFR-009 + depends_on: + - VR-220 + summary: Herstel VacatureRadar als zelfstandig productmerk met een nieuw professioneel radarlogo en favicon, en + plaats de aangeleverde ITWorx.tech-identiteit uitsluitend als subtiele ontwikkelaarscredit. + acceptance_criteria: + - VacatureRadar gebruikt een zelfstandig, vectorvriendelijk beeldmerk dat radar, detectie en een gevonden kans + combineert, scherp blijft op 16 px en visueel niet met het ITWorx-cloud-check-logo concurreert. + - Sidebar, login, favicon en Dockerman tonen het VacatureRadar-productmerk; ITWorx.tech staat alleen op geschikte + secundaire plaatsen als `Ontwikkeld door ITWorx.tech`. + - De ontwikkelaarscredit gebruikt de officiële aangeleverde ITWorx-wordmark lokaal en blijft ondergeschikt, toegankelijk + en leesbaar in donker/licht thema en op mobiel. + - Browsercontrole op login, dashboard en verkenner bij 390, 1280 en 1920 px toont correcte merkhiërarchie, geladen + assets, geen overflow of consolefouten; live container en alle assets zijn healthy. + verification: + - Inspecteer VacatureRadar-SVG op viewBox, elementeenvoud en klein-formaat-signatuur + - uv run pytest tests/integration/test_auth_login.py tests/integration/test_views.py tests/unit/test_deployment_configuration.py + - Browsermatrix 390/1280/1920 voor login, dashboard en verkenner + - Live logo-, favicon-, ontwikkelaarswordmark-, readiness- en containerhealth-smoke + - ./scripts/codex_verify.sh + primary_paths: + - static/favicon.svg + - static/img/vacatureradar-logo.svg + - static/img/vacatureradar-docker.png + - static/img/itworx-wordmark.png + - templates/base.html + - templates/registration + - static/css/premium.css + - static/css/auth.css + result: + completed_at: '2026-07-26' + note: VacatureRadar heeft een zelfstandig professioneel open-radar-logo, SVG-favicon en 256px Dockermanicoon; + officiële ITWorx.tech-wordmark staat alleen als Ontwikkeld door-credit. Twee beeldconcepten verkend, finale + geometrie handmatig als vijf-elementen-SVG gebouwd. Browsermatrix 390/1280/1920 schoon, 267 tests groen en + live image sha256:4184aeff33759b7a205637b0a0333b751b599141fe691a61965d2f1c56ca984a healthy met alle asset-smokes. +- id: VR-222 + title: Voer een integrale premium visuele audit en herstelroadmap uit + status: done + priority: P1 + requirement_ids: + - NFR-004 + - NFR-008 + - NFR-009 + depends_on: + - VR-221 + summary: Audit de volledige cockpit als één designsysteem en herstel merkhiërarchie, uitlijning, informatiedichtheid, + interactiedoelen, responsief gedrag en motion tot een consistente premium productervaring. + acceptance_criteria: + - Een gedateerd auditrapport bevat geprioriteerde bevindingen, concreet gevolg, herstelactie en volledige roadmap + voor alle hoofdschermen, login en herstel. + - Topbar en hoofdcontent delen voorspelbare horizontale ankers; dashboard en activiteit zijn aantoonbaar compacter + zonder echte informatie te verbergen of controles onbruikbaar te maken. + - VacatureRadar blijft het enige primaire appmerk; ITWorx.tech staat uitsluitend als subtiele ontwikkelaarscredit + en geen concept- of chroma-keybeeld wordt als productasset geleverd. + - Controls, focus, reduced motion, dark/light en 390/768/1280/1440/1920/3840 px zijn gecontroleerd zonder overflow, + consolefouten of regressie in de server-rendered kernflow. + - De live container is healthy en levert het finale logo, favicon en de relevante CSS met HTTP 200. + verification: + - Browsermatrix voor alle hoofdschermen plus login in dark/light en 390/768/1280/1440/1920/3840 px + - uv run pytest tests/integration/test_auth_login.py tests/integration/test_views.py + - ./scripts/codex_verify.sh + - Live readiness-, containerhealth- en assetsmokes + primary_paths: + - docs/design/VISUAL_AUDIT_2026-07-26.md + - templates/base.html + - templates/dashboard/today.html + - templates/activity/list.html + - static/css/premium.css + - static/css/pages.css + - static/css/responsive.css + result: + completed_at: '2026-07-26' + note: 'Integrale audit en roadmap uitgevoerd: gedeeld contentframe, vijf dashboardmatches, compacte activiteit, + subtiele ITWorx-credit, grotere klikdoelen, volledige reduced-motion, CSS-cacheversies en herstelde mobiele + loginiconen/uitlijning. Browsermatrix 50 route/breedtechecks plus light/login schoon; 268 tests groen, 2 skips, + 83,45% dekking.' - id: VR-301 title: Multi-tenant SaaS, teams en billing status: deferred diff --git a/docs/ai/PROJECT_STATE.md b/docs/ai/PROJECT_STATE.md index d45168b..2bd310a 100644 --- a/docs/ai/PROJECT_STATE.md +++ b/docs/ai/PROJECT_STATE.md @@ -34,6 +34,24 @@ Het volledige scherm- en afwijkingsrapport staat in `docs/design/STITCH_INTEGRAT ## Laatste onderhoudsaudit +VR-222 op 2026-07-26: + +- integrale visuele audit van tien hoofdroutes plus login/herstel vastgelegd in `docs/design/VISUAL_AUDIT_2026-07-26.md`, met geprioriteerde bevindingen, gevolgen en uitgevoerde roadmap; +- topbar en hoofdcontent delen één begrensd contentframe; het dagoverzicht toont maximaal vijf kernmatches en daalde op 1440 px van 2.399 naar 1.838 px; +- activiteitstijdlijn, ontwikkelaarscredit, secundaire klikdoelen en reduced-motion zijn verfijnd; CSS-imports gebruiken een gedeelde cacheversie zodat een release werkelijk alle stijllagen ververst; +- mobiele loginbotsing, foutieve oog-/demo-iconen en dubbele herstel-link zijn opgelost; VacatureRadar blijft productmerk en ITWorx.tech uitsluitend ontwikkelaarscredit; +- browsercontrole: 50 combinaties over tien routes en 390/768/1280/1920/3840 px, plus light-theme en login, zonder overflow of consolewaarschuwingen; +- volledige gate groen met 268 geslaagde tests, 2 optionele skips en 83,45% branch-aware dekking. Volgende uitvoerbare taak: geen. + +VR-221 op 2026-07-26: + +- merkarchitectuur gecorrigeerd: ITWorx.tech is de ontwikkelaar, VacatureRadar het zelfstandige productmerk; +- twee logo-concepten zijn met de ingebouwde beeldgeneratieflow verkend; de eenvoudigste radar-/kansrichting is handmatig vertaald naar een exact vijf-elementen-SVG met open cyan radarboog, scanstraal en emerald kanssignaal; +- sidebar, bovenbalk, login, favicon en Dockerman gebruiken het VacatureRadar-logo; de officiële ITWorx.tech-wordmark staat alleen als `Ontwikkeld door`-credit in secundaire zones; +- appmetadata heet opnieuw uitsluitend `VacatureRadar`; het ongebruikte ITWorx-appicoon is verwijderd en bron-/assetdocumentatie is bijgewerkt; +- browsermatrix op 390, 1280 en 1920 px toont correcte hiërarchie, geen overflow en een schone console; 29 gerichte regressies en de volledige gate zijn groen met 267 geslaagde tests, 2 optionele skips en 83,45% branch-aware dekking; +- live image `sha256:4184aeff33759b7a205637b0a0333b751b599141fe691a61965d2f1c56ca984a` via `unraid-itworx` uitgerold; container healthy, readiness HTTP 200 en favicon, productlogo, Dockermanicoon plus ITWorx-ontwikkelaarscredit live gevalideerd. Volgende uitvoerbare taak: geen. + VR-220 op 2026-07-26: - “eigen branding” is gecorrigeerd naar de door de eigenaar aangeleverde officiële ITWorx.tech-identiteit in plaats van een tijdelijke tekstuele Jens-signatuur; diff --git a/docs/design/STITCH_INTEGRATION_REPORT.md b/docs/design/STITCH_INTEGRATION_REPORT.md index 13b27b3..d46871d 100644 --- a/docs/design/STITCH_INTEGRATION_REPORT.md +++ b/docs/design/STITCH_INTEGRATION_REPORT.md @@ -122,3 +122,9 @@ De verkenner begrenst zijn resultatenkolom op ultrawide tot 880 px en geeft de r De projecteigenaar verduidelijkte dat “eigen branding” verwijst naar de officiële ITWorx.tech-identiteit. De tijdelijke tekstuele Jens-lock-up is daarom vervangen door de transparante, opgeschoonde wordmark uit het aangeleverde `ITWorx_Logo_Asset_Pack_2026-07-25.zip`. In de sidebar staat ITWorx.tech als hoofdmerk met `VacatureRadar · intelligence cockpit` als productdescriptor; de bovenbalk combineert ITWorx.tech met de bestaande routecontext. Login en wachtwoordherstel tonen eerst de officiële wordmark en vervolgens VacatureRadar als product. Het favicon/app-icon en Dockermanicoon zijn deterministische crops van het cloud-check-symbool uit dezelfde goedgekeurde wordmark, op respectievelijk 512×512 en 256×256. Er is geen nieuw beeldmerk gegenereerd of nagetekend. Alle assets zijn lokaal, transparant en gedocumenteerd in `static/img/README.md`. De browsermatrix op 390, 1280 en 1920 px bevestigt correcte wordmarkverhoudingen, geladen PNG-assets, geen documentoverflow en een schone console. + +## Gecorrigeerde merkarchitectuur (VR-221) + +Een volgende verduidelijking maakte duidelijk dat ITWorx.tech de ontwikkelaar is en niet het productmerk. VacatureRadar gebruikt daarom opnieuw een zelfstandig beeldmerk, nu herontworpen als een open cyan radarboog, heldere scanstraal en één emerald kanssignaal op een compact navy appvlak. Twee beeldgeneratieconcepten verkenden de vormtaal; de geselecteerde eenvoudige richting is vervolgens bewust handmatig als vijf-elementen-SVG opgebouwd voor exacte geometrie, schaalbaarheid en herkenning op faviconformaat. + +Sidebar, bovenbalk, login, favicon en Dockerman tonen VacatureRadar als primair merk. De officiële aangeleverde ITWorx.tech-wordmark staat alleen nog in een rustige `Ontwikkeld door`-credit tussen radarstatus en account, en onder authenticatiekaarten. De credit gebruikt een lokaal asset en concurreert niet met de producttitel. Browsercontrole op 390, 1280 en 1920 px bevestigt correcte hiërarchie, geladen logo's, geen overflow en een schone console. diff --git a/docs/design/VISUAL_AUDIT_2026-07-26.md b/docs/design/VISUAL_AUDIT_2026-07-26.md new file mode 100644 index 0000000..65f76bc --- /dev/null +++ b/docs/design/VISUAL_AUDIT_2026-07-26.md @@ -0,0 +1,44 @@ +# Visuele audit en herstelroadmap + +Datum: 26 juli 2026 +Scope: alle tien primaire cockpitroutes, login en wachtwoordherstel; dark/light; 390, 768, 1280, 1440, 1920 en 3840 px. + +## Ontwerpoordeel + +VacatureRadar heeft een onderscheidende, geloofwaardige dark-intelligencebasis. De cyan/emerald-signalen, technische typografie en datagedreven panelen passen bij het product. De vorige iteraties hebben echter lokale oplossingen op elkaar gestapeld. Daardoor zijn merkhiërarchie, informatiedichtheid en horizontale ankers niet overal één systeem. De premiumkans zit vooral in rust, reductie en voorspelbaarheid, niet in meer decoratie. + +## Bevindingen + +| Prioriteit | Bevinding | Gevolg | Herstel | +| --- | --- | --- | --- | +| P0 | De roze conceptafbeelding was een beeldgeneratie-werkachtergrond en had nooit als logoresultaat gepresenteerd mogen worden. | Merkverwarring en vertrouwensverlies. | Alleen het lokaal gecontroleerde VacatureRadar-radarlogo wordt als productasset gebruikt; geen gegenereerde conceptplaat wordt geleverd of gedeployed. | +| P1 | De bovenbalk en hoofdcontent hadden verschillende maximale breedtes op ultrawide. | Acties leken los te staan van de pagina en oogbewegingen werden te lang. | Eén gedeeld contentframe voor topbar en hoofdsecties, met een begrensde ultrawidevariant. | +| P1 | Het dashboard toonde tot acht vrijwel gelijkwaardige vacaturekaarten en werd 2.399 px hoog op 1440×900. | Een dagoverzicht voelde als een tweede resultatenlijst. | Beperk het overzicht tot vijf besliskaarten; de verkenner blijft de volledige rangschikking. | +| P1 | De activiteitstijdlijn gebruikte 256 px per gebeurtenis en werd 5.901 px hoog. | Lage informatiedichtheid, veel scrollen en zwakke scanbaarheid. | Compactere marker, kaart, detailzone en verticale tussenruimte zonder informatieverlies. | +| P1 | De ITWorx-credit stond in een wit, omkaderd vlak met relatief groot woordmerk. | De ontwikkelaarscredit concurreerde visueel met het productmerk en oogde als advertentie. | Maak de credit randloos, kleiner, minder verzadigd en secundair; VacatureRadar blijft primair. | +| P1 | Meerdere tekstlinks hadden een klikhoogte rond 26 px. | Zwakkere muis- en touchbruikbaarheid, ondanks semantisch correcte links. | Secundaire actielinks krijgen minimaal 38 px interactiehoogte; primaire controls blijven 44 px. | +| P2 | Op 1440 px gebruikt de filterconsole twee functionele rijen, maar de visuele groepering is zwak. | Zoeken, verfijnen en toepassen lezen als losse velden. | Behoud responsieve wrapping, maar versterk consolekaart, vaste gaps en consistente controlhoogtes. | +| P2 | Dashboardkaarten hadden veel interne verticale ruimte en sterke schaduwen. | De feed werd zwaarder dan de intelligence-rail. | Compactere kaartpadding en gaps; schaduw uitsluitend als elevatiesignaal. | +| P2 | De profielpagina benut slechts circa 1.040 px van 1.121 px beschikbare content. | Een enkel profiel voelt onbedoeld klein. | Bewuste brede compositie behouden, met illustratie als ondersteunend element en duidelijke primaire actie. | +| P2 | Systeemstatus en acht pipelinefasen zijn op laptopbreedte informatief maar erg dicht. | Kleine labels vertragen scanbaarheid. | Pipeline behoudt interne horizontale overflow op klein scherm; statusmetadata groepeert in consistente kolommen. | +| P2 | De UI bevat veel uppercase labels van 8–10 px. | Technische sfeer, maar zwakkere leesbaarheid op compacte of geschaalde displays. | Uppercase alleen voor korte metadata; body- en actietekst blijven minimaal 12–15 px. | +| P2 | Bewegingsreductie dekte de radarillustratie, niet alle transities en entreeanimaties. | Onvolledige reduced-motionervaring. | Globale gereduceerde motion voor animatie, transitie en smooth scrolling. | +| P3 | De accountnaam verschijnt zowel in de sidebar als in het menu; de topbar herhaalt productnaam en paginacontext. | Lichte redundantie, vooral op smaller desktop. | Sidebar blijft identiteit en navigatie; topbar blijft context, zoeken en systeemacties met compactere lock-up. | +| P3 | Lange beheerpagina’s zijn inhoudelijk correct maar hebben weinig lokale navigatie. | Gebruikers verliezen sneller hun positie in bronnen en activiteit. | Compacte rijen, sticky context waar aanwezig en paginering blijven leidend; geen verborgen inhoud. | + +## Uitgevoerde roadmap + +1. **Merkdiscipline** — productlogo en favicon strikt scheiden van ITWorx-ontwikkelaarsbranding; credit visueel terugplaatsen. +2. **Layoutfundament** — één maximaal contentframe voor topbar en pagina, met voorspelbare ankers van laptop tot ultrawide. +3. **Informatiereductie** — dashboard terugbrengen tot vijf kernmatches en activiteit compacter maken. +4. **Interactie en toegankelijkheid** — secundaire klikdoelen vergroten en reduced-motion volledig maken. +5. **Responsieve verfijning** — alle kernroutes controleren op mobiel, laptop, desktop en ultrawide, in licht en donker. +6. **Regressie en release** — templatecontracten, volledige gate, live containerhealth en assetsmokes uitvoeren. + +## Acceptatiebewijs + +- Browsermatrix: 50 combinaties over tien ingelogde routes op 390, 768, 1280, 1920 en 3840 px; geen documentoverflow of consolewaarschuwingen. +- Licht thema: dashboard en mobiele verkenner gecontroleerd; correcte thematokens, geen overflow en intacte filters. +- Authenticatie: login op 390 en 1440 px; één herstel-link, gescheiden labels, zelfstandig gerenderde lijniconen en correcte merkhiërarchie. +- Meetbare reductie: dashboard 2.399 → 1.838 px op 1440 px; kernmatches 7/8 → 5; activiteitgebeurtenis 268 → 242 px na cacheverversing. +- Repositorygate: Ruff, Django check, migratiecheck, 268 tests geslaagd, 2 optionele browsertests overgeslagen en 83,45% branch-aware dekking. diff --git a/docs/quality/TRACEABILITY_MATRIX.md b/docs/quality/TRACEABILITY_MATRIX.md index 321f6b3..46b12cf 100644 --- a/docs/quality/TRACEABILITY_MATRIX.md +++ b/docs/quality/TRACEABILITY_MATRIX.md @@ -39,7 +39,7 @@ Legenda: **I** = geïmplementeerd en getest in de huidige MVP; **P** = gedeeltel | PR-044 | I | profiel/modelversievelden | scoring/profiletests | VR-109 promptversie | | PR-045 | I | optionele Ollamaservice/fallback | `test_ai.py` | VR-109 | | PR-046 | I | geen tools + guardrails | AI/securitytests | VR-109/115 evaluaties | -| PR-050 | I | dashboard/list/skillsradar + Stitch-dominante cockpitshell/feed/rail en ultrawide compositie | viewtests, skill-insighttests, VR-119/206/208 browsermatrix | VR-114/118/119/206/208 e2e/a11y | +| PR-050 | I | dashboard/list/skillsradar + Stitch-dominante cockpitshell/feed/rail, gedeeld contentframe en auditgedreven informatiedichtheid | viewtests, skill-insighttests, VR-119/206/208/222 browsermatrix | VR-114/118/119/206/208/222 e2e/a11y | | PR-051 | I | detailview/template/provenance + scorehero/intelligence-strip | viewtests, VR-119 screenshots | VR-114/118/119 | | PR-052 | I | feedbackendpoint/UI | feedback/viewtests | — | | PR-053 | I | applicationmodel/serviceflow | `tests/integration/test_dedupe_lifecycle_feedback.py`, `tests/integration/test_applications.py` | VR-113 uitgebreid dossier | @@ -63,7 +63,7 @@ Legenda: **I** = geïmplementeerd en getest in de huidige MVP; **P** = gedeeltel | NFR-005 | I | domeinapps/services/adapters/ADRs | continue review | | NFR-006 | I | offline pytest + coveragegate | VR-114 verbreedt e2e | | 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, officiële lokale ITWorx.tech-merklaag en toegankelijke interactieve radarillustratie | `tests/e2e/test_vr114_browser_and_accessibility.py`, `tests/integration/test_intelligence_views.py`, `tests/integration/test_views.py`, `artifacts/visual-validation/`, VR-209–213, VR-216–220 | +| NFR-008 | I | volledige negen-schermen-Stitch-cockpit, acht viewports, dark/light, toetsenborddrawer, premium visuele hiërarchie, zelfstandig VacatureRadar-productmerk, lokale ITWorx-ontwikkelaarscredit en toegankelijke interactieve radarillustratie | `tests/e2e/test_vr114_browser_and_accessibility.py`, `tests/integration/test_intelligence_views.py`, `tests/integration/test_views.py`, `artifacts/visual-validation/`, VR-209–213, VR-216–221 | | NFR-009 | I | lokale SQLite, configureerbare eigenaarbranding, Docker/Unraidconfig, gevalideerde SSH-deployalias, release-smoke en restore | `tests/unit/test_deployment_configuration.py`, VR-117, VR-217/218 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 | @@ -109,3 +109,4 @@ Iedere backlogtaak die een requirement wijzigt, werkt in dezelfde commit deze ma - VR-218 verenigt favicon, login en shell onder één radarbeeldmerk, maakt de persoonlijke eigenaarssignatuur configureerbaar en voegt contextuele oriëntatie aan de bovenbalk toe zonder mobiele overflow. - VR-219 verankert alle hoofdsecties op één ultrawide grid, begrenst paneelverhoudingen voor leesbaarheid en maakt `Personal intelligence by Jens` tot de consistente productlock-up op publieke en ingelogde schermen. - VR-220 vervangt die tijdelijke tekstlock-up door de aangeleverde officiële ITWorx.tech-wordmark en een exact daaruit afgeleid cloud-check-appicoon voor browser, shell en Dockerman; VacatureRadar blijft de productnaam. +- VR-221 corrigeert de merkarchitectuur: VacatureRadar krijgt een zelfstandig radar-/kansbeeldmerk voor alle productcontactpunten en ITWorx.tech blijft uitsluitend zichtbaar als officiële secundaire ontwikkelaarscredit. diff --git a/static/css/app.css b/static/css/app.css index ad98855..7ac6df9 100644 --- a/static/css/app.css +++ b/static/css/app.css @@ -1,7 +1,9 @@ -@import url("tokens.css"); -@import url("base.css"); -@import url("layout.css"); -@import url("components.css"); -@import url("pages.css"); -@import url("responsive.css"); -@import url("premium.css"); +/* Houd ook geïmporteerde stylesheets versieerbaar; alleen app.css voorzien van + een querystring ververst anders niet noodzakelijk de browsercache. */ +@import url("tokens.css?v=0.3.9"); +@import url("base.css?v=0.3.9"); +@import url("layout.css?v=0.3.9"); +@import url("components.css?v=0.3.9"); +@import url("pages.css?v=0.3.9"); +@import url("responsive.css?v=0.3.9"); +@import url("premium.css?v=0.3.9"); diff --git a/static/css/auth.css b/static/css/auth.css index 257febe..34d8e32 100644 --- a/static/css/auth.css +++ b/static/css/auth.css @@ -125,9 +125,29 @@ body.auth-page { .auth-brand-logo { display: block; - width: min(274px, 78vw); + width: 72px; + height: 72px; + margin-bottom: 16px; + border-radius: 18px; + box-shadow: 0 16px 38px rgba(0, 124, 131, 0.16), 0 2px 6px rgba(18, 38, 43, 0.08); +} + +.auth-shell > .developer-credit { + display: flex; + align-items: center; + gap: 9px; + justify-content: center; + margin: 18px auto 0; + color: var(--on-surface-variant); + font: 700 9px/1 var(--font-sans); + letter-spacing: .1em; + text-transform: uppercase; +} + +.auth-shell > .developer-credit img { + display: block; + width: 113px; height: auto; - margin-bottom: 18px; } .auth-brand-name { @@ -221,6 +241,7 @@ body.auth-page { letter-spacing: 0.02em; color: var(--primary); text-decoration: none; + white-space: nowrap; } .field-link:hover { @@ -290,6 +311,22 @@ body.auth-page { height: 18px; } +.pw-toggle .icon[hidden] { + display: none; +} + +@media (max-width: 480px) { + .field-label-row { + align-items: flex-start; + flex-direction: column; + gap: 5px; + } + + .field-link { + font-size: 10px; + } +} + .field-error { margin-top: 6px; font-size: 12px; diff --git a/static/css/layout.css b/static/css/layout.css index 745df14..87ba304 100644 --- a/static/css/layout.css +++ b/static/css/layout.css @@ -21,7 +21,7 @@ .sidebar-account strong, .sidebar-account small { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .sidebar-account small { font: 10px var(--font-mono); text-transform: uppercase; } .cockpit-workspace { min-height: 100vh; margin-left: var(--sidebar-width); } -.cockpit-topbar { position: fixed; inset: 0 0 auto var(--sidebar-width); z-index: 40; display: flex; height: var(--topbar-height); align-items: center; justify-content: space-between; gap: 20px; padding: 0 var(--content-gutter); border-bottom: 1px solid color-mix(in srgb, var(--outline) 32%, transparent); background: color-mix(in srgb, var(--surface) 88%, transparent); backdrop-filter: blur(18px); } +.cockpit-topbar { position: fixed; inset: 0 0 auto var(--sidebar-width); z-index: 40; display: flex; height: var(--topbar-height); align-items: center; padding: 0 var(--content-gutter); border-bottom: 1px solid color-mix(in srgb, var(--outline) 32%, transparent); background: color-mix(in srgb, var(--surface) 88%, transparent); backdrop-filter: blur(18px); } .topbar-start, .topbar-actions { display: flex; min-width: 0; align-items: center; gap: 12px; } .topbar-start { flex: 1; } .cockpit-main { width: 100%; min-height: 100vh; padding: calc(var(--topbar-height) + 36px) var(--content-gutter) 56px; } diff --git a/static/css/pages.css b/static/css/pages.css index 62ffd06..9477ac4 100644 --- a/static/css/pages.css +++ b/static/css/pages.css @@ -300,18 +300,18 @@ .platform-grid { margin-top: 18px; } .platform-grid article { align-items: flex-start; flex-direction: column; } .activity-filterbar { display: grid; grid-template-columns: minmax(260px, 1fr) 220px auto; align-items: end; gap: 10px; margin-bottom: 28px; padding: 14px; border: 1px solid color-mix(in srgb, var(--outline) 42%, transparent); background: var(--surface-low); } -.activity-timeline { position: relative; display: grid; gap: 16px; padding-left: 78px; } -.activity-timeline::before { position: absolute; inset: 0 auto 0 28px; width: 1px; content: ""; background: linear-gradient(var(--cyan), var(--outline) 70%, transparent); opacity: .45; } +.activity-timeline { position: relative; display: grid; gap: 10px; padding-left: 62px; } +.activity-timeline::before { position: absolute; inset: 0 auto 0 22px; width: 1px; content: ""; background: linear-gradient(var(--cyan), var(--outline) 70%, transparent); opacity: .45; } .activity-event { position: relative; } -.activity-marker { position: absolute; top: 0; left: -78px; display: grid; width: 58px; height: 58px; place-items: center; border: 1px solid var(--outline); background: var(--surface-low); color: var(--text-muted); } +.activity-marker { position: absolute; top: 0; left: -62px; display: grid; width: 44px; height: 44px; place-items: center; border: 1px solid var(--outline); border-radius: 10px; background: var(--surface-low); color: var(--text-muted); } .activity-marker.is-positive { border-color: color-mix(in srgb, var(--emerald) 45%, var(--outline)); color: var(--emerald); } .activity-marker.is-attention { border-color: color-mix(in srgb, var(--coral) 45%, var(--outline)); color: var(--coral); } -.activity-card { padding: 20px; border: 1px solid color-mix(in srgb, var(--outline) 44%, transparent); background: var(--surface-container); } +.activity-card { padding: 16px 18px; border: 1px solid color-mix(in srgb, var(--outline) 44%, transparent); border-radius: 12px; background: var(--surface-container); } .activity-card-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 20px; } -.activity-card-head h2 { margin: 9px 0 0; } +.activity-card-head h2 { margin: 6px 0 0; font-size: 18px; } .activity-card-head time { color: var(--text-faint); font: 11px var(--font-mono); } .activity-object { color: var(--text-muted); } -.decision-note { margin: 14px 0; padding: 12px 14px; border-left: 2px solid var(--cyan); background: var(--surface-high); } +.decision-note { margin: 10px 0; padding: 9px 12px; border-left: 2px solid var(--cyan); background: var(--surface-high); } .decision-note p { margin: 7px 0 0; } .employer-kpis { display: flex; gap: 8px; } .employer-kpis > div { min-width: 130px; padding: 11px 13px; border: 1px solid color-mix(in srgb, var(--outline) 42%, transparent); background: var(--surface-low); } diff --git a/static/css/premium.css b/static/css/premium.css index 39b49ad..4498471 100644 --- a/static/css/premium.css +++ b/static/css/premium.css @@ -7,6 +7,7 @@ --radius-sm: 7px; --radius-md: 12px; --radius-lg: 18px; + --content-frame: 1720px; } :root[data-theme="light"] { @@ -34,10 +35,16 @@ body::before { .cockpit-sidebar { background: color-mix(in srgb, var(--surface-lowest) 97%, black); } .cockpit-main { padding-top: calc(var(--topbar-height) + 42px); } -.cockpit-main > * { max-width: 2800px; margin-right: auto; margin-left: 0; } -.brand-lockup { display: grid; min-width: 0; gap: 5px; } -.brand-wordmark { display: block; width: 174px; height: auto; padding: 5px 8px; border-radius: 7px; background: rgba(255, 255, 255, .94); } -.cockpit-brand small { margin: 0 2px; color: var(--text-faint); font-size: 8px; font-weight: 700; letter-spacing: .08em; } +.cockpit-main > * { width: min(100%, var(--content-frame)); margin-right: auto; margin-left: 0; } +.topbar-inner { display: flex; width: min(100%, var(--content-frame)); height: 100%; align-items: center; justify-content: space-between; gap: 20px; } +.brand-radar { flex: 0 0 auto; width: 42px; height: 42px; border-radius: 11px; box-shadow: 0 10px 28px color-mix(in srgb, var(--cyan) 12%, transparent); } +.brand-lockup { display: grid; min-width: 0; gap: 4px; } +.brand-lockup strong { color: var(--text-strong); font: 740 17px/1 var(--font-headline); letter-spacing: -.025em; } +.cockpit-brand small { margin: 0; color: var(--text-faint); font-size: 8px; font-weight: 700; letter-spacing: .09em; } +.developer-credit { display: flex; align-items: center; gap: 7px; color: var(--text-faint); font: 650 7px/1 var(--font-body); letter-spacing: .08em; text-transform: uppercase; } +.developer-credit img { display: block; width: 92px; height: auto; opacity: .82; } +.cockpit-sidebar > .developer-credit { margin: 0 18px 10px; padding: 8px 0 0; border-top: 1px solid var(--divider); } +.cockpit-sidebar > .developer-credit img { padding: 2px 4px; border-radius: 3px; background: rgba(255, 255, 255, .9); filter: saturate(.82); } .workspace-identity { display: grid; min-width: 176px; padding-right: 18px; border-right: 1px solid var(--divider); line-height: 1.1; } .workspace-identity span { color: var(--text-strong); font-size: 14px; font-weight: 740; letter-spacing: -.015em; } .workspace-identity small { margin-top: 5px; color: var(--text-faint); font: 650 9px var(--font-body); letter-spacing: .075em; text-transform: uppercase; } @@ -62,6 +69,7 @@ body::before { .section-kicker, .kpi-label { color: var(--text-faint); font-family: var(--font-body); font-weight: 700; letter-spacing: .105em; } .section-kicker { margin-bottom: 7px; } +.section-link { display: inline-flex; min-height: 38px; align-items: center; padding: 5px 2px; } .button { min-height: 44px; border-radius: 9px; } .button-primary { box-shadow: 0 8px 24px color-mix(in srgb, var(--cyan) 13%, transparent); } @@ -114,6 +122,9 @@ label > span:first-child { font-family: var(--font-body); font-weight: 700; lett .rail-panel:not(.radar-summary) { background: color-mix(in srgb, var(--surface-raised) 76%, var(--surface-low)); box-shadow: none; } .job-card { transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease; } .job-card:hover { transform: translateY(-2px); border-color: var(--divider-strong); box-shadow: var(--shadow-card); } +.dashboard-matches .job-feed { gap: 12px; } +.dashboard-matches .feed-card { min-height: 0; } +.dashboard-matches .job-card-main { padding: 18px 20px; } .filter-console { grid-template-columns: minmax(280px, 1.8fr) repeat(5, minmax(126px, .72fr)) auto; @@ -163,6 +174,7 @@ label > span:first-child { font-family: var(--font-body); font-weight: 700; lett .empty-state { border-color: var(--divider-strong); border-radius: var(--radius-md); } @media (min-width: 1800px) { + :root { --content-frame: 1880px; } .explorer-shell { grid-template-columns: minmax(650px, 880px) minmax(760px, 1fr); } .automation-layout { grid-template-columns: minmax(0, 1fr) minmax(340px, 430px); } } @@ -188,6 +200,7 @@ label > span:first-child { font-family: var(--font-body); font-weight: 700; lett .profile-index-art { min-height: 230px; border-top: 1px solid var(--divider); border-left: 0; } .profile-index-art .radar-illustration { width: 220px; } .command-search:focus-within { width: min(100%, 510px); } + .topbar-inner { gap: 8px; } } @media (max-width: 560px) { @@ -219,5 +232,6 @@ label > span:first-child { font-family: var(--font-body); font-weight: 700; lett } @media (prefers-reduced-motion: reduce) { + *, *::before, *::after { scroll-behavior: auto !important; animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; } .radar-illustration { transform: none !important; transition: none; } } diff --git a/static/favicon.svg b/static/favicon.svg new file mode 100644 index 0000000..c244518 --- /dev/null +++ b/static/favicon.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/static/img/README.md b/static/img/README.md index a881283..bed26c4 100644 --- a/static/img/README.md +++ b/static/img/README.md @@ -1,7 +1,7 @@ # Lokale merkassets -`itworx-wordmark.png` is de transparante, opgeschoonde officiële ITWorx.tech-wordmark uit `ITWorx_Logo_Asset_Pack_2026-07-25.zip`, door de projecteigenaar aangeleverd op 2026-07-26. +`itworx-wordmark.png` is de transparante, opgeschoonde officiële ITWorx.tech-wordmark uit `ITWorx_Logo_Asset_Pack_2026-07-25.zip`, door de projecteigenaar aangeleverd op 2026-07-26. Deze wordmark wordt uitsluitend gebruikt voor de secundaire credit `Ontwikkeld door ITWorx.tech`; ITWorx is niet het productmerk. -`itworx-app-icon.png` en `vacatureradar-docker.png` zijn deterministisch uitgesneden en met Lanczos geschaalde varianten van het cloud-check-symbool uit dezelfde goedgekeurde wordmark. Ze bevatten geen nieuw of gegenereerd beeldmerk. Het app-icon is 512×512; de Dockermanvariant is 256×256. +`vacatureradar-logo.svg` is het zelfstandige productbeeldmerk: een open radarboog, scanstraal en één gevonden kans. Het concept is met de ingebouwde beeldgeneratieflow verkend en vervolgens handmatig als eenvoudig schaalbaar SVG opgebouwd. `favicon.svg` gebruikt exact dezelfde geometrie en `vacatureradar-docker.png` is de 256×256 rastervariant. Deze assets zijn uitsluitend voor de eigen VacatureRadar-installatie en worden niet als generiek herbruikbare of publiek gelicenseerde merkset aangeboden. diff --git a/static/img/itworx-app-icon.png b/static/img/itworx-app-icon.png deleted file mode 100644 index 315d8bd..0000000 Binary files a/static/img/itworx-app-icon.png and /dev/null differ diff --git a/static/img/vacatureradar-docker.png b/static/img/vacatureradar-docker.png index 54191c8..e5ff9c6 100644 Binary files a/static/img/vacatureradar-docker.png and b/static/img/vacatureradar-docker.png differ diff --git a/static/img/vacatureradar-logo.svg b/static/img/vacatureradar-logo.svg new file mode 100644 index 0000000..c244518 --- /dev/null +++ b/static/img/vacatureradar-logo.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/templates/base.html b/templates/base.html index 33c6841..4dc7a13 100644 --- a/templates/base.html +++ b/templates/base.html @@ -7,9 +7,9 @@ - + {% block title %}{{ app_name }}{% endblock %} - + {% block extra_head %}{% endblock %} @@ -21,8 +21,9 @@
+ {% include "components/developer_credit.html" %} @@ -69,7 +69,7 @@ {% if entra_enabled %} @@ -100,7 +100,7 @@ {% csrf_token %} {% if next %}{% endif %} @@ -108,12 +108,11 @@ {% endif %} + {% include "components/developer_credit.html" %} + diff --git a/templates/registration/password_reset_complete.html b/templates/registration/password_reset_complete.html index a928810..4cf7d31 100644 --- a/templates/registration/password_reset_complete.html +++ b/templates/registration/password_reset_complete.html @@ -5,14 +5,14 @@ Wachtwoord gewijzigd · VacatureRadar - + {% include "components/icon_sprite.html" %}
- +

VacatureRadar

Persoonlijke intelligence cockpit

@@ -30,6 +30,7 @@ + {% include "components/developer_credit.html" %}
diff --git a/templates/registration/password_reset_confirm.html b/templates/registration/password_reset_confirm.html index 5d7f426..d98bf48 100644 --- a/templates/registration/password_reset_confirm.html +++ b/templates/registration/password_reset_confirm.html @@ -5,14 +5,14 @@ Nieuw wachtwoord · VacatureRadar - + {% include "components/icon_sprite.html" %}
- +

VacatureRadar

Persoonlijke intelligence cockpit

@@ -59,6 +59,7 @@ Nieuwe link aanvragen {% endif %} + {% include "components/developer_credit.html" %}
diff --git a/templates/registration/password_reset_done.html b/templates/registration/password_reset_done.html index b0fc06e..8b78849 100644 --- a/templates/registration/password_reset_done.html +++ b/templates/registration/password_reset_done.html @@ -5,14 +5,14 @@ Herstel-link verstuurd · VacatureRadar - + {% include "components/icon_sprite.html" %}
- +

VacatureRadar

Persoonlijke intelligence cockpit

@@ -25,6 +25,7 @@ Terug naar inloggen + {% include "components/developer_credit.html" %}
diff --git a/templates/registration/password_reset_form.html b/templates/registration/password_reset_form.html index 4a2b5f0..0f17ac4 100644 --- a/templates/registration/password_reset_form.html +++ b/templates/registration/password_reset_form.html @@ -5,14 +5,14 @@ Wachtwoord herstellen · VacatureRadar - + {% include "components/icon_sprite.html" %}
- +

VacatureRadar

Persoonlijke intelligence cockpit

@@ -40,6 +40,7 @@ Terug naar inloggen + {% include "components/developer_credit.html" %}
diff --git a/tests/integration/test_auth_login.py b/tests/integration/test_auth_login.py index 7fdb436..560311c 100644 --- a/tests/integration/test_auth_login.py +++ b/tests/integration/test_auth_login.py @@ -26,9 +26,13 @@ def test_login_page_renders_stitch_design(client): assert 'src="/static/js/login.js?' in body assert "