131 lines
4.2 KiB
HTML
131 lines
4.2 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}Dashboard — CalibreSync{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="page-header">
|
|
<h1>Dashboard</h1>
|
|
<div class="header-actions">
|
|
{% if interval > 0 %}
|
|
<span class="schedule-badge">
|
|
Auto every {{ interval }}m
|
|
{% if next_run %} — next: {{ next_run }}{% endif %}
|
|
</span>
|
|
{% endif %}
|
|
<form method="post" action="/sync/rescan" style="display:inline">
|
|
<button class="btn btn-secondary" {% if sync_running %}disabled{% endif %}>Rescan remote</button>
|
|
</form>
|
|
<form method="post" action="/sync" style="display:inline">
|
|
{% if sync_running %}
|
|
<button class="btn btn-disabled" disabled>Sync running…</button>
|
|
{% else %}
|
|
<button class="btn btn-primary" {% if cache_info.count == 0 %}title="Run 'Rescan remote' first to populate the zip list"{% endif %}>Run Sync Now</button>
|
|
{% endif %}
|
|
</form>
|
|
{% if not sync_running %}
|
|
<form method="post" action="/sync/test" style="display:inline">
|
|
<button class="btn btn-secondary">Test (1 zip)</button>
|
|
</form>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% if batch_size > 0 %}
|
|
<p class="muted small" style="margin-bottom:1.5rem">Batch size: {{ batch_size }} zips per chunk — full sync processes all unprocessed files.</p>
|
|
{% endif %}
|
|
|
|
{% if request.query_params.get("started") %}
|
|
<div class="alert alert-success">Sync started — processing all unprocessed archives{% if batch_size > 0 %} in batches of {{ batch_size }}{% endif %}.</div>
|
|
{% endif %}
|
|
{% if request.query_params.get("test_started") %}
|
|
<div class="alert alert-success">Test sync started — processing 1 archive.</div>
|
|
{% endif %}
|
|
{% if request.query_params.get("rescan_started") %}
|
|
<div class="alert alert-success">Remote rescan started — this will take a few minutes. Check logs for progress.</div>
|
|
{% endif %}
|
|
{% if request.query_params.get("already_running") %}
|
|
<div class="alert alert-warning">A sync is already running.</div>
|
|
{% endif %}
|
|
|
|
<div class="cache-status">
|
|
{% if cache_info.count > 0 %}
|
|
<span class="muted small">Remote cache: <strong>{{ cache_info.count }}</strong> zip(s) — last scanned {{ cache_info.last_scan[:19] if cache_info.last_scan else "never" }} UTC</span>
|
|
{% else %}
|
|
<span class="muted small" style="color:var(--warning)">Remote cache empty — first sync will run a full scan (may take several minutes).</span>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="stats-grid">
|
|
<div class="stat-card">
|
|
<div class="stat-value">{{ stats.total_zips }}</div>
|
|
<div class="stat-label">Zip archives processed</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-value">{{ stats.total_imported }}</div>
|
|
<div class="stat-label">Books imported</div>
|
|
</div>
|
|
</div>
|
|
|
|
<h2>Recent sync runs</h2>
|
|
{% if runs %}
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Started</th>
|
|
<th>Finished</th>
|
|
<th>Status</th>
|
|
<th>New zips</th>
|
|
<th>Imported</th>
|
|
<th>Errors</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for r in runs %}
|
|
<tr>
|
|
<td>{{ r.started_at[:19].replace("T"," ") }}</td>
|
|
<td>{{ r.finished_at[:19].replace("T"," ") if r.finished_at else "—" }}</td>
|
|
<td><span class="badge badge-{{ r.status }}">{{ r.status }}</span></td>
|
|
<td>{{ r.zips_new }}</td>
|
|
<td>{{ r.books_imported }}</td>
|
|
<td>{{ r.books_errored }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
{% else %}
|
|
<p class="muted">No sync runs yet. Click "Run Sync" to start.</p>
|
|
{% endif %}
|
|
|
|
<h2>Recent zip archives</h2>
|
|
{% if zips %}
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Remote path</th>
|
|
<th>Size</th>
|
|
<th>Processed</th>
|
|
<th>Status</th>
|
|
<th>Error</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for z in zips %}
|
|
<tr>
|
|
<td class="mono">{{ z.remote_path }}</td>
|
|
<td>{{ (z.file_size / 1048576) | round(1) }} MB</td>
|
|
<td>{{ z.processed_at[:19].replace("T"," ") if z.processed_at else "—" }}</td>
|
|
<td><span class="badge badge-{{ z.status }}">{{ z.status }}</span></td>
|
|
<td class="muted small">{{ z.error_msg or "" }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
{% else %}
|
|
<p class="muted">No zip archives processed yet.</p>
|
|
{% endif %}
|
|
|
|
{% if sync_running %}
|
|
<script>
|
|
setTimeout(() => location.reload(), 5000);
|
|
</script>
|
|
{% endif %}
|
|
{% endblock %}
|