feat: establish Stitch cockpit frontend contract

This commit is contained in:
Jens
2026-07-22 19:48:36 +02:00
parent a30ecbc553
commit 09af98193f
17 changed files with 764 additions and 941 deletions
+74 -75
View File
@@ -5,92 +5,91 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="dark light">
<meta name="theme-color" content="#07101f">
<meta name="theme-color" content="#060e20">
<title>{% block title %}{{ app_name }}{% endblock %}</title>
<link rel="icon" href="{% static 'favicon.svg' %}" type="image/svg+xml">
<link rel="stylesheet" href="{% static 'css/app.css' %}?v={{ app_version }}">
<script src="{% static 'js/app.js' %}?v={{ app_version }}" defer></script>
{% block extra_head %}{% endblock %}
</head>
<body>
<body class="{% block body_class %}{% endblock %}">
{% include "components/icon_sprite.html" %}
<a class="skip-link" href="#main">Naar hoofdinhoud</a>
{% if user.is_authenticated %}
<div class="app-frame" data-app-frame>
<aside class="sidebar" data-sidebar aria-label="Hoofdnavigatie">
<div class="sidebar-brand">
<a class="brand" href="{% url 'dashboard:today' %}">
<span class="brand-mark" aria-hidden="true"><svg class="icon"><use href="#icon-radar"></use></svg></span>
<span><strong>VacatureRadar</strong><small>Intelligence cockpit</small></span>
</a>
<button class="icon-button sidebar-close mobile-only" type="button" data-nav-close aria-label="Menu sluiten">
<svg class="icon"><use href="#icon-close"></use></svg>
</button>
<div class="cockpit" data-app-frame>
<aside class="cockpit-sidebar" data-sidebar aria-label="Hoofdnavigatie">
<div class="sidebar-heading">
<a class="cockpit-brand" href="{% url 'dashboard:today' %}" aria-label="VacatureRadar dashboard">
<span class="brand-radar" aria-hidden="true"><svg class="icon"><use href="#icon-radar"></use></svg></span>
<span><strong>VacatureRadar</strong><small>Intelligence cockpit</small></span>
</a>
<button class="icon-button nav-close mobile-only" type="button" data-nav-close aria-label="Menu sluiten"><svg class="icon"><use href="#icon-close"></use></svg></button>
</div>
<nav class="cockpit-nav">
<p class="nav-caption">Intelligence</p>
<a class="nav-item {% if request.resolver_match.namespace == 'dashboard' and request.resolver_match.url_name == 'today' %}is-active{% endif %}" href="{% url 'dashboard:today' %}"><svg class="icon"><use href="#icon-dashboard"></use></svg><span>Overzicht</span></a>
<a class="nav-item {% if request.resolver_match.namespace == 'jobs' and request.resolver_match.url_name == 'list' %}is-active{% endif %}" href="{% url 'jobs:list' %}"><svg class="icon"><use href="#icon-radar"></use></svg><span>Nieuwe matches</span></a>
<a class="nav-item {% if request.resolver_match.namespace == 'jobs' and request.resolver_match.url_name == 'detail' %}is-active{% endif %}" href="{% url 'jobs:list' %}?focus=all"><svg class="icon"><use href="#icon-briefcase"></use></svg><span>Alle vacatures</span></a>
<a class="nav-item {% if request.resolver_match.namespace == 'jobs' and request.resolver_match.url_name == 'skill-insights' %}is-active{% endif %}" href="{% url 'jobs:skill-insights' %}"><svg class="icon"><use href="#icon-chart"></use></svg><span>Skillsradar</span></a>
<a class="nav-item {% if request.resolver_match.namespace == 'jobs' and request.resolver_match.url_name == 'applications' or request.resolver_match.url_name == 'application-edit' %}is-active{% endif %}" href="{% url 'jobs:applications' %}"><svg class="icon"><use href="#icon-pipeline"></use></svg><span>Sollicitaties</span></a>
<p class="nav-caption">Radarbeheer</p>
<a class="nav-item {% if request.resolver_match.namespace == 'profiles' %}is-active{% endif %}" href="{% url 'profiles:list' %}"><svg class="icon"><use href="#icon-profile"></use></svg><span>Zoekprofiel</span></a>
<a class="nav-item {% if request.resolver_match.namespace == 'sources' %}is-active{% endif %}" href="{% url 'sources:list' %}"><svg class="icon"><use href="#icon-sources"></use></svg><span>Bronnen</span></a>
<a class="nav-item {% if request.resolver_match.namespace == 'dashboard' and request.resolver_match.url_name == 'system' %}is-active{% endif %}" href="{% url 'dashboard:system' %}"><svg class="icon"><use href="#icon-system"></use></svg><span>Automatisering</span></a>
</nav>
<div class="sidebar-radar-card">
<span class="status-beacon {% if navigation_profile %}is-live{% endif %}" aria-hidden="true"></span>
<span><strong>{% if navigation_profile %}Radar actief{% else %}Profiel nodig{% endif %}</strong><small>{% if navigation_profile %}{{ navigation_profile.name }}{% else %}Configureer je zoekprofiel{% endif %}</small></span>
</div>
<div class="sidebar-account">
<span class="avatar" aria-hidden="true">{{ user.first_name|default:user.username|slice:":1"|upper }}</span>
<span><strong>{{ user.get_full_name|default:user.username }}</strong><small>Persoonlijke radar</small></span>
</div>
</aside>
<button class="nav-scrim" type="button" data-nav-scrim aria-label="Menu sluiten" tabindex="-1"></button>
<div class="cockpit-workspace">
<header class="cockpit-topbar">
<div class="topbar-start">
<button class="icon-button mobile-only" type="button" data-nav-toggle aria-label="Menu openen" aria-expanded="false"><svg class="icon"><use href="#icon-menu"></use></svg></button>
<form class="command-search" method="get" action="{% url 'jobs:list' %}" role="search">
<label class="sr-only" for="global-search">Zoek door vacatures</label>
<svg class="icon" aria-hidden="true"><use href="#icon-search"></use></svg>
<input id="global-search" type="search" name="q" value="{% if request.resolver_match.namespace == 'jobs' and request.resolver_match.url_name == 'list' %}{{ request.GET.q }}{% endif %}" placeholder="Zoek vacatures, werkgevers of skills…">
<kbd aria-hidden="true">/</kbd>
</form>
</div>
<nav class="nav-stack">
<p class="nav-group-label">Radar</p>
<a class="nav-link {% if request.resolver_match.namespace == 'dashboard' and request.resolver_match.url_name == 'today' %}active{% endif %}" href="{% url 'dashboard:today' %}">
<svg class="icon"><use href="#icon-dashboard"></use></svg><span>Vandaag<small>beste matches</small></span>
</a>
<a class="nav-link {% if request.resolver_match.namespace == 'jobs' and request.resolver_match.url_name == 'list' %}active{% endif %}" href="{% url 'jobs:list' %}">
<svg class="icon"><use href="#icon-search"></use></svg><span>Vacatures<small>zoeken en filteren</small></span>
</a>
<a class="nav-link {% if request.resolver_match.namespace == 'jobs' and request.resolver_match.url_name == 'skill-insights' %}active{% endif %}" href="{% url 'jobs:skill-insights' %}">
<svg class="icon"><use href="#icon-radar"></use></svg><span>Skillsradar<small>vraag en leerkansen</small></span>
</a>
<a class="nav-link {% if request.resolver_match.namespace == 'jobs' and request.resolver_match.url_name == 'applications' or request.resolver_match.url_name == 'application-edit' %}active{% endif %}" href="{% url 'jobs:applications' %}">
<svg class="icon"><use href="#icon-pipeline"></use></svg><span>Sollicitaties<small>persoonlijke pipeline</small></span>
</a>
<p class="nav-group-label">Beheer</p>
<a class="nav-link {% if request.resolver_match.namespace == 'profiles' %}active{% endif %}" href="{% url 'profiles:list' %}">
<svg class="icon"><use href="#icon-profile"></use></svg><span>Zoekprofiel<small>regels en voorkeuren</small></span>
</a>
<a class="nav-link {% if request.resolver_match.namespace == 'sources' %}active{% endif %}" href="{% url 'sources:list' %}">
<svg class="icon"><use href="#icon-sources"></use></svg><span>Bronnen<small>import en gezondheid</small></span>
</a>
<a class="nav-link {% if request.resolver_match.namespace == 'dashboard' and request.resolver_match.url_name == 'system' %}active{% endif %}" href="{% url 'dashboard:system' %}">
<svg class="icon"><use href="#icon-system"></use></svg><span>Systeem<small>readiness en beheer</small></span>
</a>
</nav>
<div class="sidebar-user">
<span class="avatar" aria-hidden="true">{{ user.username|slice:":1"|upper }}</span>
<span><strong>{{ user.get_full_name|default:user.username }}</strong><small>Lokale gebruiker</small></span>
<span class="status-dot" title="Aangemeld"></span>
<div class="topbar-actions">
<a class="system-live" href="{% url 'dashboard:system' %}"><span class="status-beacon is-live" aria-hidden="true"></span><span class="desktop-label">Systeemstatus</span></a>
<button class="icon-button" type="button" data-theme-toggle aria-label="Licht thema activeren" title="Thema wisselen"><svg class="icon theme-icon-dark"><use href="#icon-sun"></use></svg><svg class="icon theme-icon-light"><use href="#icon-moon"></use></svg></button>
<details class="account-menu">
<summary aria-label="Gebruikersmenu openen"><span class="avatar avatar-small" aria-hidden="true">{{ user.first_name|default:user.username|slice:":1"|upper }}</span><svg class="icon desktop-label"><use href="#icon-chevron"></use></svg></summary>
<div class="account-popover"><strong>{{ user.get_full_name|default:user.username }}</strong><small>{{ user.username }}</small>{% if navigation_profile %}<a href="{% url 'profiles:edit' navigation_profile.pk %}">Zoekprofiel aanpassen</a>{% endif %}<form action="{% url 'logout' %}" method="post">{% csrf_token %}<button type="submit"><svg class="icon"><use href="#icon-logout"></use></svg>Afmelden</button></form></div>
</details>
</div>
</aside>
<button class="nav-scrim" type="button" data-nav-scrim aria-label="Menu sluiten" tabindex="-1"></button>
<div class="workspace">
<header class="utilitybar">
<div class="utilitybar-start">
<button class="icon-button mobile-only" type="button" data-nav-toggle aria-label="Menu openen" aria-expanded="false">
<svg class="icon"><use href="#icon-menu"></use></svg>
</button>
<form class="global-search" method="get" action="{% url 'jobs:list' %}" role="search">
<label class="sr-only" for="global-search">Zoek door vacatures</label>
<svg class="icon" aria-hidden="true"><use href="#icon-search"></use></svg>
<input id="global-search" type="search" name="q" value="{% if request.resolver_match.namespace == 'jobs' and request.resolver_match.url_name == 'list' %}{{ request.GET.q }}{% endif %}" placeholder="Zoek door vacatures...">
</form>
<div class="utility-title utility-radar"><span class="signal-dot" aria-hidden="true"></span><span>Radar actief</span></div>
</div>
<div class="utility-actions">
<button class="icon-button" type="button" data-theme-toggle aria-label="Thema wisselen" title="Thema wisselen">
<svg class="icon theme-icon-dark"><use href="#icon-sun"></use></svg>
<svg class="icon theme-icon-light"><use href="#icon-moon"></use></svg>
</button>
{% if navigation_profile %}<a class="button button-primary utility-cta" href="{% url 'profiles:edit' navigation_profile.pk %}"><svg class="icon"><use href="#icon-profile"></use></svg><span class="desktop-label">Zoekprofiel aanpassen</span></a>{% endif %}
<form action="{% url 'logout' %}" method="post">{% csrf_token %}<button class="utility-link" type="submit" aria-label="Afmelden"><svg class="icon"><use href="#icon-logout"></use></svg><span class="desktop-label">Afmelden</span></button></form>
</div>
</header>
<main id="main" class="main-content" tabindex="-1">
</header>
<main id="main" class="cockpit-main" tabindex="-1">
{% if messages %}<div class="toast-stack" aria-live="polite" aria-atomic="true">{% for message in messages %}<div class="toast {{ message.tags }}"><svg class="icon" aria-hidden="true"><use href="{% if message.tags == 'error' %}#icon-alert{% else %}#icon-check{% endif %}"></use></svg><span>{{ message }}</span></div>{% endfor %}</div>{% endif %}
{% else %}
<main id="main" class="auth-main" tabindex="-1">
<main id="main" class="auth-shell" tabindex="-1">
{% if messages %}<div class="toast-stack" aria-live="polite">{% for message in messages %}<div class="toast {{ message.tags }}">{{ message }}</div>{% endfor %}</div>{% endif %}
{% endif %}
{% if messages %}
<div class="messages" aria-live="polite" aria-atomic="true">
{% for message in messages %}<div class="message {{ message.tags }}">{{ message }}</div>{% endfor %}
</div>
{% endif %}
{% block content %}{% endblock %}
</main>
{% if user.is_authenticated %}</div></div>{% endif %}
{% block content %}{% endblock %}
</main>
{% if user.is_authenticated %}
<nav class="mobile-tabbar" aria-label="Mobiele hoofdnavigatie">
<a class="{% if request.resolver_match.namespace == 'dashboard' %}is-active{% endif %}" href="{% url 'dashboard:today' %}"><svg class="icon"><use href="#icon-dashboard"></use></svg><span>Overzicht</span></a>
<a class="{% if request.resolver_match.namespace == 'jobs' and request.resolver_match.url_name == 'list' %}is-active{% endif %}" href="{% url 'jobs:list' %}"><svg class="icon"><use href="#icon-radar"></use></svg><span>Matches</span></a>
<a class="{% if request.resolver_match.url_name == 'applications' %}is-active{% endif %}" href="{% url 'jobs:applications' %}"><svg class="icon"><use href="#icon-pipeline"></use></svg><span>Pipeline</span></a>
<a class="{% if request.resolver_match.namespace == 'profiles' %}is-active{% endif %}" href="{% url 'profiles:list' %}"><svg class="icon"><use href="#icon-profile"></use></svg><span>Profiel</span></a>
</nav>
</div>
</div>
{% endif %}
{% block extra_js %}{% endblock %}
</body>
</html>