feat: add automation activity and employer intelligence

This commit is contained in:
Jens
2026-07-22 20:17:20 +02:00
parent 4fa2a34447
commit afe18e5b96
16 changed files with 679 additions and 18 deletions
+8
View File
@@ -0,0 +1,8 @@
{% extends "base.html" %}
{% block title %}{{ employer.name }} intelligence · VacatureRadar{% endblock %}
{% block content %}
<a class="back-link" href="{% url 'jobs:employers' %}">← Terug naar werkgeversradar</a>
<header class="detail-hero employer-detail-hero"><div><p class="eyebrow">Employer intelligence #{{ employer.pk }}</p><div class="title-row">{% if employer.is_recruiter %}<span class="pill">Recruiter</span>{% elif employer.is_direct_employer %}<span class="pill pill-success">Directe werkgever</span>{% endif %}{% if employer.employer_type %}<span class="pill">{{ employer.employer_type }}</span>{% endif %}</div><h1>{{ employer.name }}</h1><p class="detail-employer">{{ employer.domain|default:"Domein onbekend" }}</p></div><span class="employer-mark hero-mark">{{ employer.name|slice:":2"|upper }}</span></header>
<section class="intelligence-strip"><div><span class="field-label">Actieve vacatures</span><strong>{{ employer.active_vacancies }}</strong><small>alle functies</small></div><div><span class="field-label">Relevante IT</span><strong>{{ employer.relevant_vacancies }}</strong><small>deterministisch gefilterd</small></div><div><span class="field-label">Gemiddelde match</span><strong>{% if employer.average_match is not None %}{{ employer.average_match|floatformat:0 }}%{% else %}—{% endif %}</strong><small>{% if active_profile %}{{ active_profile.name }}{% else %}geen actief profiel{% endif %}</small></div><div><span class="field-label">Brondekking</span><strong>{{ employer.source_count }}</strong><small>unieke bronnen</small></div></section>
<div class="employer-detail-grid"><main><section class="detail-section"><div class="section-heading"><div><span class="section-kicker">Vacaturehistorie</span><h2>Recente vacatures</h2><p>Actief en historisch, uitsluitend uit opgeslagen bronrecords.</p></div></div><div class="employer-job-grid">{% for job in recent_jobs %}<article><div class="title-row"><span class="pill pill-{{ job.status }}">{{ job.get_status_display }}</span>{% if job.match_recommendation %}<span class="pill pill-{{ job.match_recommendation }}">{{ job.match_recommendation }}</span>{% endif %}</div><h3><a href="{% url 'jobs:detail' job.pk %}">{{ job.original_title }}</a></h3><p>{{ job.raw_location|default:"Locatie onbekend" }} · {{ job.get_workplace_type_display }}</p><div class="job-meta"><span>{{ job.first_seen|date:"d/m/Y" }}</span>{% if job.match_score is not None %}<strong>{{ job.match_score|floatformat:0 }}% match</strong>{% endif %}</div></article>{% empty %}<div class="empty-state compact-empty"><h2>Geen vacaturehistorie</h2></div>{% endfor %}</div></section>{% if match_history %}<section class="detail-section"><span class="section-kicker">Matchpatroon</span><h2>Recente profielbeoordelingen</h2><div class="match-history">{% for score in match_history %}<div><span>{{ score.created_at|date:"d/m" }}</span><i style="--match: {{ score.score }}%"></i><strong>{{ score.score|floatformat:0 }}%</strong><a href="{% url 'jobs:detail' score.job.pk %}">{{ score.job.original_title }}</a></div>{% endfor %}</div></section>{% endif %}</main><aside class="detail-sidebar"><section class="panel"><span class="section-kicker">Waargenomen context</span><h2>Werkgeversprofiel</h2><dl class="intel-facts"><div><dt>Locaties</dt><dd>{% if locations %}{{ locations|join:", " }}{% else %}Onbekend{% endif %}</dd></div><div><dt>Type</dt><dd>{{ employer.employer_type|default:"Onbekend" }}</dd></div><div><dt>Laatste signaal</dt><dd>{{ employer.latest_job_at|date:"d/m/Y"|default:"—" }}</dd></div><div><dt>Confidence</dt><dd>{% widthratio employer.confidence 1 100 %}%</dd></div></dl></section><section class="panel"><span class="section-kicker">Brongezondheid</span><h2>{{ sources|length }} gekoppelde bronnen</h2>{% for source in sources %}<a class="employer-source" href="{% url 'sources:list' %}"><span>{{ source.name }}</span><span class="pill {% if source.status == 'active' %}pill-success{% else %}pill-review{% endif %}">{{ source.get_status_display }}</span></a>{% empty %}<p class="muted">Geen gekoppelde bron opgeslagen.</p>{% endfor %}</section><section class="panel"><span class="section-kicker">Eerdere sollicitaties</span><h2>{{ applications|length }} dossier{% if applications|length != 1 %}s{% endif %}</h2>{% for application in applications %}<a class="employer-source" href="{% url 'jobs:application-edit' application.pk %}"><span>{{ application.job.original_title }}</span><span>{{ application.get_status_display }}</span></a>{% empty %}<p class="muted">Nog geen dossier bij deze werkgever.</p>{% endfor %}</section></aside></div>
{% endblock %}
+11
View File
@@ -0,0 +1,11 @@
{% extends "base.html" %}
{% block title %}Werkgevers intelligence · VacatureRadar{% endblock %}
{% block content %}
<header class="cockpit-heading"><div><p class="eyebrow">Intelligent tracking</p><h1>Werkgeversradar</h1><p>Genormaliseerde werkgeversdata uit echte vacatures, bronnen, profielscores en sollicitatiedossiers.</p></div><div class="employer-kpis"><div><span>Gemonitord</span><strong>{{ employer_total }}</strong></div><div><span>Met actieve vacatures</span><strong>{{ employer_active_total }}</strong></div></div></header>
<form class="activity-filterbar" method="get"><label class="grow"><span class="field-label">Zoek werkgevers</span><span class="input-with-icon"><svg class="icon"><use href="#icon-search"></use></svg><input type="search" name="q" value="{{ request.GET.q }}" placeholder="Naam, domein of locatie"></span></label><label><span class="field-label">Kanaal</span><select name="channel"><option value="">Alle kanalen</option><option value="direct" {% if request.GET.channel == 'direct' %}selected{% endif %}>Directe werkgever</option><option value="recruiter" {% if request.GET.channel == 'recruiter' %}selected{% endif %}>Recruiter</option></select></label><label class="toggle-field"><input type="checkbox" name="active" value="1" {% if request.GET.active == '1' %}checked{% endif %}><span>Alleen actief</span></label><button class="button button-secondary" type="submit">Filter</button></form>
<div class="employer-radar-layout">
<section class="employer-table-panel" aria-labelledby="employer-list-heading"><div class="section-heading"><div><span class="section-kicker">Gevolgde werkgevers</span><h2 id="employer-list-heading">{{ page_obj.paginator.count }} werkgevers</h2></div></div><div class="data-table-wrap" tabindex="0" aria-label="Werkgevers, horizontaal scrolbaar"><table class="data-table employer-table"><thead><tr><th>Werkgever</th><th>Type</th><th>Actief</th><th>Relevant IT</th><th>Bronnen</th><th>Gem. match</th><th>Laatste signaal</th></tr></thead><tbody>{% for item in employers %}<tr class="{% if selected_employer.pk == item.pk %}is-selected{% endif %}"><td><a href="?selected={{ item.pk }}&q={{ request.GET.q|urlencode }}&channel={{ request.GET.channel }}&active={{ request.GET.active }}"><span class="employer-mark">{{ item.name|slice:":2"|upper }}</span><span><strong>{{ item.name }}</strong><small>{{ item.domain|default:"Geen domein" }}</small></span></a></td><td>{% if item.is_recruiter %}<span class="pill">Recruiter</span>{% elif item.is_direct_employer %}<span class="pill pill-success">Direct</span>{% else %}<span class="pill">Onbekend</span>{% endif %}</td><td>{{ item.active_vacancies }}</td><td><strong class="signal-number">{{ item.relevant_vacancies }}</strong></td><td>{{ item.source_count }}</td><td>{% if item.average_match is not None %}{{ item.average_match|floatformat:0 }}%{% else %}—{% endif %}</td><td>{{ item.latest_job_at|date:"d/m/Y"|default:"—" }}</td></tr>{% empty %}<tr><td colspan="7">Geen werkgevers gevonden.</td></tr>{% endfor %}</tbody></table></div>{% if is_paginated %}<nav class="pagination" aria-label="Werkgeverspaginering">{% if page_obj.has_previous %}<a class="button button-ghost" href="?page={{ page_obj.previous_page_number }}&q={{ request.GET.q|urlencode }}&channel={{ request.GET.channel }}&active={{ request.GET.active }}">Vorige</a>{% endif %}<span>{{ page_obj.number }}/{{ page_obj.paginator.num_pages }}</span>{% if page_obj.has_next %}<a class="button button-ghost" href="?page={{ page_obj.next_page_number }}&q={{ request.GET.q|urlencode }}&channel={{ request.GET.channel }}&active={{ request.GET.active }}">Volgende</a>{% endif %}</nav>{% endif %}</section>
<aside class="employer-intelligence-rail" aria-label="Geselecteerde werkgever">{% if selected_employer %}<div class="employer-intel-header"><span class="section-kicker">Employer report #{{ selected_employer.pk }}</span><span class="employer-mark large">{{ selected_employer.name|slice:":2"|upper }}</span><h2>{{ selected_employer.name }}</h2><p>{{ selected_employer.domain|default:"Domein onbekend" }}</p></div><dl class="intel-facts"><div><dt>Actieve vacatures</dt><dd>{{ selected_employer.active_vacancies }}</dd></div><div><dt>Relevante IT</dt><dd>{{ selected_employer.relevant_vacancies }}</dd></div><div><dt>Eerdere dossiers</dt><dd>{{ selected_employer.previous_applications }}</dd></div><div><dt>Bronnen</dt><dd>{{ selected_employer.source_count }}</dd></div><div><dt>Gem. match</dt><dd>{% if selected_employer.average_match is not None %}{{ selected_employer.average_match|floatformat:0 }}%{% else %}Nog niet gemeten{% endif %}</dd></div><div><dt>Kanaal</dt><dd>{% if selected_employer.is_recruiter %}Recruiter{% elif selected_employer.is_direct_employer %}Direct{% else %}Onbekend{% endif %}</dd></div></dl><section class="employer-intel-section"><span class="field-label">Recente vacatures</span>{% for job in selected_detail.recent_jobs|slice:":4" %}<a class="employer-job-link" href="{% url 'jobs:detail' job.pk %}"><span><strong>{{ job.original_title }}</strong><small>{{ job.first_seen|date:"d/m/Y" }}</small></span>{% if job.match_score is not None %}<b>{{ job.match_score|floatformat:0 }}%</b>{% endif %}</a>{% empty %}<p class="muted">Nog geen vacaturehistorie.</p>{% endfor %}</section><section class="employer-intel-section"><span class="field-label">Waargenomen locaties</span><p>{% if selected_detail.locations %}{{ selected_detail.locations|join:", " }}{% else %}Onbekend{% endif %}</p></section><a class="button button-primary button-block" href="{% url 'jobs:employer-detail' selected_employer.pk %}">Volledig werkgeversrapport</a>{% else %}<div class="empty-state"><h2>Kies een werkgever</h2></div>{% endif %}</aside>
</div>
{% endblock %}