db7c102da1
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
105 lines
3.7 KiB
HTML
105 lines
3.7 KiB
HTML
{% extends "base.html" %}
|
|
{% block content %}
|
|
<div class="p-6 max-w-3xl mx-auto" x-data="exportApp('{{ album_id }}')">
|
|
<h1 class="text-2xl font-bold mb-4">Export</h1>
|
|
<div class="stats shadow mb-6">
|
|
<div class="stat"><div class="stat-title">Ready to export</div>
|
|
<div class="stat-value text-primary">{{ to_export | length }}</div></div>
|
|
<div class="stat"><div class="stat-title">Skipped</div>
|
|
<div class="stat-value opacity-40">{{ skipped | length }}</div></div>
|
|
</div>
|
|
|
|
<div class="space-y-2 mb-6">
|
|
{% for group in to_export %}
|
|
<div class="export-item card card-compact bg-base-100 shadow">
|
|
<div class="card-body">
|
|
<p class="font-semibold">{{ group.title }}</p>
|
|
<p class="text-xs opacity-60">{{ group.date }} · {{ group.entry_type }} · {{ group.photo_ids | length }} photos</p>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
<button id="export-btn" class="btn btn-primary" @click="runExport()">
|
|
Export {{ to_export | length }} entries
|
|
</button>
|
|
|
|
<!-- Overwrite confirmation modal -->
|
|
<dialog id="overwrite-modal" class="modal">
|
|
<div class="modal-box">
|
|
<h3 class="font-bold">Destination exists</h3>
|
|
<p x-text="overwriteMsg" class="py-2 text-sm"></p>
|
|
<div class="modal-action">
|
|
<button class="btn btn-warning btn-sm" @click="confirmOverwrite()">Overwrite</button>
|
|
<button class="btn btn-ghost btn-sm" @click="cancelExport()">Cancel</button>
|
|
</div>
|
|
</div>
|
|
</dialog>
|
|
|
|
<!-- Results -->
|
|
<div x-show="successMsg !== ''" class="mt-6 alert alert-success text-sm" x-text="successMsg"></div>
|
|
<div x-show="failedCount > 0" class="mt-2 alert alert-warning text-sm"
|
|
x-text="`${failedCount} photo(s) failed to download`"></div>
|
|
|
|
<details class="mt-6">
|
|
<summary class="cursor-pointer text-sm opacity-60 skipped-list">
|
|
Skipped ({{ skipped | length }}) — not exported
|
|
</summary>
|
|
<ul class="mt-2 space-y-1 text-sm opacity-60">
|
|
{% for g in skipped %}<li>{{ g.title or g.date }}</li>{% endfor %}
|
|
</ul>
|
|
</details>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_scripts %}
|
|
<script>
|
|
function exportApp(albumId) {
|
|
return {
|
|
successMsg: '',
|
|
failedCount: 0,
|
|
conflictPath: null,
|
|
overwriteMsg: '',
|
|
|
|
async runExport() {
|
|
const res = await fetch('/export/run', {
|
|
method: 'POST', headers: {'Content-Type':'application/json'},
|
|
body: JSON.stringify({album_id: albumId}),
|
|
});
|
|
const data = await res.json();
|
|
if (data.conflict) {
|
|
this.conflictPath = data.path;
|
|
this.overwriteMsg = `Destination already exists: ${data.path}`;
|
|
document.getElementById('overwrite-modal').showModal();
|
|
} else if (data.ok) {
|
|
this.successMsg = `Exported ${data.exported} entr${data.exported === 1 ? 'y' : 'ies'} successfully.`;
|
|
this.failedCount = (data.failed || []).length;
|
|
}
|
|
},
|
|
|
|
async confirmOverwrite() {
|
|
document.getElementById('overwrite-modal').close();
|
|
const res = await fetch('/export/overwrite', {
|
|
method: 'POST', headers: {'Content-Type':'application/json'},
|
|
body: JSON.stringify({album_id: albumId, path: this.conflictPath}),
|
|
});
|
|
const data = await res.json();
|
|
if (data.conflict) {
|
|
this.conflictPath = data.path;
|
|
this.overwriteMsg = `Destination already exists: ${data.path}`;
|
|
document.getElementById('overwrite-modal').showModal();
|
|
} else if (data.ok) {
|
|
this.successMsg = `Exported ${data.exported} entr${data.exported === 1 ? 'y' : 'ies'} successfully.`;
|
|
this.failedCount = (data.failed || []).length;
|
|
}
|
|
},
|
|
|
|
cancelExport() {
|
|
document.getElementById('overwrite-modal').close();
|
|
this.conflictPath = null;
|
|
},
|
|
};
|
|
}
|
|
</script>
|
|
{% endblock %}
|