Initial commit
This commit is contained in:
commit
536ff43ce4
158 files changed
+30355
No files matched your search
@@ -0,0 +1,159 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Mails verplaatsen{% endblock %}
|
||||
{% block page_title %}Mails verplaatsen{% endblock %}
|
||||
{% block main_class %}medium{% endblock %}
|
||||
{% block loader_text %}Bezig met Google...{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
<style>
|
||||
.stappen { display: grid; gap: 14px; }
|
||||
.rij { display: flex; flex-wrap: wrap; gap: 10px; align-items: end; }
|
||||
.rij .field { flex: 1 1 260px; margin: 0; }
|
||||
.modus { display: flex; flex-wrap: wrap; gap: 10px; }
|
||||
.modus label { flex: 1 1 240px; display: flex; gap: 10px; align-items: flex-start; padding: 12px 14px; margin: 0;
|
||||
border: 1px solid var(--border); border-radius: var(--radius); cursor: pointer; font-weight: 400; }
|
||||
.modus label:has(input:checked) { border-color: var(--primary-color); background: var(--primary-light); }
|
||||
.modus input { width: auto; margin-top: 3px; }
|
||||
.modus strong { display: block; }
|
||||
.samenvatting { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; justify-content: space-between;
|
||||
padding: 14px 16px; border-radius: var(--radius); background: var(--surface-muted); margin-top: 16px; }
|
||||
.samenvatting .pijl { font-size: 1.3em; color: var(--text-muted, #64748b); }
|
||||
.voortgang { height: 10px; border-radius: 999px; background: var(--surface-muted); overflow: hidden; margin: 10px 0 6px; }
|
||||
.voortgang div { height: 100%; width: 0; background: var(--primary-color); transition: width .4s; }
|
||||
.tellers { display: flex; flex-wrap: wrap; gap: 14px; font-size: .92em; }
|
||||
td.nowrap { white-space: nowrap; }
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="card">
|
||||
<h2 class="card-title">🏷️ Berichten met een label naar een andere mailbox</h2>
|
||||
<p class="field-hint" style="margin-top:-6px">Kies de mailbox en het label. PIVOT kopieert elk bericht met dat label (met bijlagen,
|
||||
originele datum, gelezen/ongelezen en ster) naar de doelmailbox, onder een label met dezelfde naam.</p>
|
||||
|
||||
<form method="GET" action="{{ url_for('gmail_labels.overzicht') }}" class="stappen" onsubmit="showLoader()">
|
||||
<div class="rij">
|
||||
<div class="field">
|
||||
<label for="bron">1. Bronmailbox</label>
|
||||
<input type="email" id="bron" name="bron" required value="{{ bron.email if bron else bron_in }}" placeholder="bv. secretariaat@school.be">
|
||||
</div>
|
||||
<button type="submit" class="btn-secondary">🔍 Labels ophalen</button>
|
||||
</div>
|
||||
|
||||
{% if bron %}
|
||||
<div class="muted small" style="margin-top:-6px">📬 {{ bron.naam or bron.email }} · {{ bron.email }}{% if bron.ou %} · {{ bron.ou }}{% endif %}</div>
|
||||
{% if labels %}
|
||||
<div class="field" style="margin:0">
|
||||
<label for="label">2. Label</label>
|
||||
<select id="label" name="label" required>
|
||||
<option value="">— kies een label —</option>
|
||||
{% for l in labels %}
|
||||
<option value="{{ l.id }}" {{ 'selected' if gekozen and gekozen.id == l.id }}>{{ l.naam }}{% if l.aantal is not none %} ({{ l.aantal }}){% endif %}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="field" style="margin:0">
|
||||
<label for="doel">3. Doelmailbox</label>
|
||||
<input type="email" id="doel" name="doel" required value="{{ doel.email if doel else doel_in }}" placeholder="bv. directie@school.be">
|
||||
</div>
|
||||
<div><button type="submit" class="btn-secondary">Controleren</button></div>
|
||||
{% elif not fout %}
|
||||
<p class="muted">Deze mailbox heeft geen eigen labels.</p>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</form>
|
||||
|
||||
{% if fout %}<div class="alert alert-danger" style="margin-top:14px">{{ fout }}</div>{% endif %}
|
||||
|
||||
{% if bron and doel and gekozen %}
|
||||
<form method="POST" action="{{ url_for('gmail_labels.start') }}" style="margin-top:18px"
|
||||
onsubmit="const v = this.modus.value === 'verplaatsen'; return confirm({{ (gekozen.aantal|string if gekozen.aantal is not none else 'Alle') |tojson|forceescape }} + ' bericht(en) met label ' + {{ gekozen.naam|tojson|forceescape }} + (v ? ' VERPLAATSEN' : ' kopiëren') + ' van ' + {{ bron.email|tojson|forceescape }} + ' naar ' + {{ doel.email|tojson|forceescape }} + '?' + (v ? '\n\nIn de bronmailbox gaan ze naar de prullenbak (30 dagen terug te halen).' : ''));">
|
||||
<input type="hidden" name="bron" value="{{ bron.email }}">
|
||||
<input type="hidden" name="doel" value="{{ doel.email }}">
|
||||
<input type="hidden" name="label" value="{{ gekozen.id }}">
|
||||
<label style="margin-bottom:8px">4. Wat moet er gebeuren?</label>
|
||||
<div class="modus">
|
||||
<label><input type="radio" name="modus" value="kopieren" checked>
|
||||
<span><strong>📋 Kopiëren</strong><span class="muted small">De berichten blijven ook in de bronmailbox staan.</span></span></label>
|
||||
<label><input type="radio" name="modus" value="verplaatsen">
|
||||
<span><strong>📦 Verplaatsen</strong><span class="muted small">Na het kopiëren gaan ze in de bronmailbox naar de prullenbak.</span></span></label>
|
||||
</div>
|
||||
<div class="samenvatting">
|
||||
<span><strong>{{ gekozen.aantal if gekozen.aantal is not none else '?' }}</strong> bericht(en) met label <strong>{{ gekozen.naam }}</strong></span>
|
||||
<span>{{ bron.email }} <span class="pijl">→</span> {{ doel.email }}</span>
|
||||
<button type="submit" class="btn-success" {{ 'disabled' if status.bezig }}>▶ Starten</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if status.bezig or status.resultaat or status.fout %}
|
||||
<div class="card" id="voortgangKaart">
|
||||
<h3 class="card-title">⏳ Opdracht</h3>
|
||||
{% if status.opdracht %}<p class="muted small" id="opdracht">Label <strong>{{ status.opdracht.label }}</strong>:
|
||||
{{ status.opdracht.bron }} → {{ status.opdracht.doel }} ({{ 'verplaatsen' if status.opdracht.modus == 'verplaatsen' else 'kopiëren' }})</p>{% endif %}
|
||||
<div class="voortgang"><div id="balk"></div></div>
|
||||
<div class="tellers">
|
||||
<span><b id="tVoortgang">{{ status.voortgang }}</b> / <b id="tTotaal">{{ status.totaal }}</b></span>
|
||||
<span>✅ <b id="tGelukt">{{ status.gelukt }}</b> gelukt</span>
|
||||
<span>⏭️ <b id="tOver">{{ status.overgeslagen }}</b> stonden er al</span>
|
||||
<span>⚠️ <b id="tFout">{{ status.fouten }}</b> mislukt</span>
|
||||
</div>
|
||||
<div class="alert alert-success" id="resultaat" style="margin-top:12px" {{ '' if status.resultaat and not status.bezig else 'hidden' }}>{{ status.resultaat or '' }}</div>
|
||||
<div class="alert alert-danger" id="foutmelding" style="margin-top:12px" {{ '' if status.fout else 'hidden' }}>{{ status.fout or '' }}</div>
|
||||
<ul class="muted small" id="foutvoorbeelden">{% for f in status.foutvoorbeelden %}<li>{{ f }}</li>{% endfor %}</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if geschiedenis %}
|
||||
<div class="card">
|
||||
<h3 class="card-title">🕑 Vorige opdrachten</h3>
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead><tr><th>Gestart</th><th>Door</th><th>Label</th><th>Van → naar</th><th>Soort</th><th class="text-right">Gelukt</th><th class="text-right">Mislukt</th></tr></thead>
|
||||
<tbody>
|
||||
{% for h in geschiedenis %}
|
||||
<tr title="{{ h.Melding or '' }}">
|
||||
<td class="nowrap">{{ h.Gestart.strftime('%d-%m-%Y %H:%M') }}{% if not h.Klaar %} <span class="badge badge-warning">bezig</span>{% endif %}</td>
|
||||
<td>{{ h.Door or '' }}</td>
|
||||
<td><strong>{{ h.Label }}</strong></td>
|
||||
<td class="small">{{ h.Bron }} → {{ h.Doel }}</td>
|
||||
<td>{{ 'Verplaatst' if h.Modus == 'verplaatsen' else 'Gekopieerd' }}</td>
|
||||
<td class="text-right">{{ h.Gelukt if h.Gelukt is not none else '' }}{% if h.Overgeslagen %} <span class="muted small">(+{{ h.Overgeslagen }})</span>{% endif %}</td>
|
||||
<td class="text-right">{{ h.Fouten or '' }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<p class="field-hint">Eenmalig nodig in de Google Admin-console (Beveiliging › API-beheer › Domeinbrede delegatie, bij het
|
||||
serviceaccount van PIVOT): het bereik <code>{{ scope }}</code>. Een onderbroken opdracht kan je gewoon opnieuw starten:
|
||||
berichten die al gekopieerd werden, worden overgeslagen.</p>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
(function () {
|
||||
const kaart = document.getElementById('voortgangKaart');
|
||||
if (!kaart) return;
|
||||
const zet = (id, w) => document.getElementById(id).textContent = w;
|
||||
function kijk() {
|
||||
fetch({{ url_for('gmail_labels.status')|tojson }}, { credentials: 'same-origin' }).then(r => r.json()).then(s => {
|
||||
zet('tVoortgang', s.voortgang); zet('tTotaal', s.totaal); zet('tGelukt', s.gelukt);
|
||||
zet('tOver', s.overgeslagen); zet('tFout', s.fouten);
|
||||
document.getElementById('balk').style.width = (s.totaal ? Math.round(100 * s.voortgang / s.totaal) : (s.bezig ? 3 : 100)) + '%';
|
||||
const res = document.getElementById('resultaat'), fout = document.getElementById('foutmelding');
|
||||
res.hidden = !(s.resultaat && !s.bezig); res.textContent = s.resultaat || '';
|
||||
fout.hidden = !s.fout; fout.textContent = s.fout || '';
|
||||
document.getElementById('foutvoorbeelden').innerHTML = (s.foutvoorbeelden || []).map(f => `<li>${f.replace(/</g, '<')}</li>`).join('');
|
||||
if (s.bezig) setTimeout(kijk, 2000);
|
||||
}).catch(() => setTimeout(kijk, 5000));
|
||||
}
|
||||
kijk();
|
||||
})();
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in new issue
Block a user