initial commit
All checks were successful
Build and Push Image / build-and-push (push) Successful in 1m26s
All checks were successful
Build and Push Image / build-and-push (push) Successful in 1m26s
This commit is contained in:
parent
3bba1f6db6
commit
b56e866071
36 changed files with 4160 additions and 0 deletions
223
app/templates/statistics.html
Normal file
223
app/templates/statistics.html
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Statistieken - Snauw Counter{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>📈 Statistieken & Trends</h2>
|
||||
|
||||
<!-- Statistiek overzicht -->
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card">
|
||||
<span class="stat-number">{{ stats.total_incidents }}</span>
|
||||
<span class="stat-label">Totaal incidenten</span>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<span class="stat-number">{{ stats.incidents_today }}</span>
|
||||
<span class="stat-label">Vandaag</span>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<span class="stat-number">{{ stats.avg_severity }}</span>
|
||||
<span class="stat-label">Gemiddelde Snauw-index</span>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<span class="stat-number">{{ stats.most_severe }}</span>
|
||||
<span class="stat-label">Hoogste Snauw-index</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Trend grafiek -->
|
||||
{% if stats.total_incidents > 0 %}
|
||||
<div class="card">
|
||||
<h3>📊 Trend laatste 7 dagen</h3>
|
||||
<div class="chart-container">
|
||||
<canvas id="trendChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Periode statistieken -->
|
||||
<div class="card">
|
||||
<h3>📅 Periode overzicht</h3>
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card">
|
||||
<span class="stat-number">{{ stats.incidents_this_week }}</span>
|
||||
<span class="stat-label">Deze week</span>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<span class="stat-number">{{ stats.incidents_this_month }}</span>
|
||||
<span class="stat-label">Deze maand</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recent incidents -->
|
||||
<div class="card">
|
||||
<h3>🕒 Recente incidenten</h3>
|
||||
{% if incidents %}
|
||||
<div style="max-height: 300px; overflow-y: auto;">
|
||||
{% for incident in incidents[:10] %}
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; padding: 0.5rem 0; border-bottom: 1px solid #e9ecef;">
|
||||
<div>
|
||||
<strong>Snauw-index {{ incident.severity }}/5</strong>
|
||||
{% if incident.notes %}
|
||||
<br><small style="color: #6c757d;">{{ incident.notes[:50] }}{% if incident.notes|length > 50 %}...{% endif %}</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
<small style="color: #6c757d;">
|
||||
{{ incident.timestamp.strftime('%d/%m %H:%M') }}
|
||||
</small>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% if incidents|length > 10 %}
|
||||
<p style="margin-top: 1rem; color: #6c757d; text-align: center;">
|
||||
En {{ incidents|length - 10 }} andere incidenten...
|
||||
</p>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<p style="color: #6c757d;">Nog geen incidenten geregistreerd</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
<!-- Geen data yet -->
|
||||
<div class="card" style="text-align: center; padding: 3rem;">
|
||||
<h3>📊 Nog geen statistieken</h3>
|
||||
<p style="color: #6c757d; margin-bottom: 2rem;">
|
||||
Registreer je eerste incident om statistieken te zien
|
||||
</p>
|
||||
<a href="{{ url_for('main.index') }}" class="btn">📝 Eerste incident toevoegen</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- GDPR compliance section -->
|
||||
<div class="card" style="border-left: 4px solid #ffc107;">
|
||||
<h4>🔒 Privacy & Gegevensbeheer</h4>
|
||||
<p>Je hebt altijd controle over je gegevens:</p>
|
||||
|
||||
<div style="display: flex; gap: 0.5rem; flex-wrap: wrap; margin-top: 1rem;">
|
||||
<button onclick="exportData()" class="btn btn-secondary">📥 Data exporteren</button>
|
||||
|
||||
{% if current_user.is_authenticated %}
|
||||
<form action="{{ url_for('main.delete_account') }}" method="POST" style="display: inline;"
|
||||
onsubmit="return confirm('Type DELETE om te bevestigen') && prompt('Type DELETE:') === 'DELETE'">
|
||||
<input type="hidden" name="confirm" value="DELETE">
|
||||
<button type="submit" class="btn btn-danger">🗑️ Account verwijderen</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<button onclick="deleteAllData()" class="btn btn-danger">🗑️ Alle data wissen</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<p style="margin-top: 1rem; font-size: 0.9rem; color: #6c757d;">
|
||||
<a href="{{ url_for('main.privacy') }}" style="color: inherit;">Privacy beleid</a> |
|
||||
Conform AVG/GDPR
|
||||
</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
// Mark body as authenticated for JavaScript
|
||||
document.body.dataset.isAuthenticated = {{ 'true' if current_user.is_authenticated else 'false' }};
|
||||
|
||||
{% if stats.total_incidents > 0 %}
|
||||
// Chart.js implementation
|
||||
const trendData = {{ stats.trend_data | tojson }};
|
||||
|
||||
const ctx = document.getElementById('trendChart').getContext('2d');
|
||||
new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: trendData.map(d => d.date),
|
||||
datasets: [{
|
||||
label: 'Aantal incidenten',
|
||||
data: trendData.map(d => d.count),
|
||||
backgroundColor: 'rgba(102, 126, 234, 0.6)',
|
||||
borderColor: 'rgba(102, 126, 234, 1)',
|
||||
borderWidth: 2,
|
||||
yAxisID: 'y'
|
||||
}, {
|
||||
label: 'Gemiddelde Snauw-index',
|
||||
data: trendData.map(d => d.avg_severity),
|
||||
type: 'line',
|
||||
borderColor: 'rgba(220, 53, 69, 1)',
|
||||
backgroundColor: 'rgba(220, 53, 69, 0.1)',
|
||||
borderWidth: 3,
|
||||
fill: false,
|
||||
yAxisID: 'y1'
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Incident Trends (Laatste 7 dagen)'
|
||||
},
|
||||
legend: {
|
||||
display: true
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
type: 'linear',
|
||||
display: true,
|
||||
position: 'left',
|
||||
beginAtZero: true,
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Aantal incidenten'
|
||||
}
|
||||
},
|
||||
y1: {
|
||||
type: 'linear',
|
||||
display: true,
|
||||
position: 'right',
|
||||
min: 0,
|
||||
max: 5,
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Gemiddelde Snauw-index'
|
||||
},
|
||||
grid: {
|
||||
drawOnChartArea: false,
|
||||
}
|
||||
}
|
||||
},
|
||||
interaction: {
|
||||
mode: 'index',
|
||||
intersect: false,
|
||||
}
|
||||
}
|
||||
});
|
||||
{% endif %}
|
||||
|
||||
// Load anonymous user data if not authenticated
|
||||
if (!{{ 'true' if current_user.is_authenticated else 'false' }}) {
|
||||
// This would enhance the statistics with localStorage data
|
||||
enhanceWithLocalData();
|
||||
}
|
||||
|
||||
function enhanceWithLocalData() {
|
||||
// For anonymous users, we could enhance server statistics
|
||||
// with localStorage data that might not be synced yet
|
||||
const localIncidents = JSON.parse(localStorage.getItem('snauw_incidents') || '[]');
|
||||
|
||||
if (localIncidents.length > 0) {
|
||||
// Add notification about local data
|
||||
const notice = document.createElement('div');
|
||||
notice.className = 'alert alert-info';
|
||||
notice.innerHTML = `
|
||||
ℹ️ Je hebt ${localIncidents.length} lokaal opgeslagen incidenten.
|
||||
<a href="{{ url_for('main.register') }}">Maak een account</a> om deze veilig op te slaan.
|
||||
`;
|
||||
document.querySelector('main').insertBefore(notice, document.querySelector('main').firstChild);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
Loading…
Add table
Add a link
Reference in a new issue