feat: show i.O. photos in review, lightbox on thumbnail click
This commit is contained in:
97
index.html
97
index.html
@@ -50,6 +50,17 @@
|
||||
.stat { display: flex; justify-content: space-between; padding: 0.5rem 0; border-bottom: 1px solid #222; font-size: 0.95rem; }
|
||||
.stat-value { font-weight: 700; color: #e94560; }
|
||||
.hint { margin-top: 1rem; color: #888; font-size: 0.85rem; }
|
||||
.section-divider { font-size: 0.8rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: #555; margin: 1.2rem 0 0.5rem; }
|
||||
.badge-reject { font-size: 0.75rem; font-weight: 600; color: #e94560; background: rgba(233,69,96,0.12); border-radius: 4px; padding: 0.15rem 0.45rem; margin-left: 0.4rem; }
|
||||
.badge-ok { font-size: 0.75rem; font-weight: 600; color: #4caf7d; background: rgba(76,175,125,0.12); border-radius: 4px; padding: 0.15rem 0.45rem; margin-left: 0.4rem; }
|
||||
.photo-item img { cursor: zoom-in; }
|
||||
/* Lightbox */
|
||||
.lightbox { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.88); z-index: 1000; align-items: center; justify-content: center; }
|
||||
.lightbox.open { display: flex; }
|
||||
.lightbox img { max-width: 90vw; max-height: 90vh; border-radius: 8px; object-fit: contain; box-shadow: 0 8px 40px rgba(0,0,0,0.6); }
|
||||
.lightbox-close { position: absolute; top: 1.2rem; right: 1.5rem; font-size: 2rem; color: #fff; cursor: pointer; line-height: 1; opacity: 0.7; }
|
||||
.lightbox-close:hover { opacity: 1; }
|
||||
.lightbox-name { position: absolute; bottom: 1.5rem; color: #ccc; font-size: 0.9rem; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -135,11 +146,28 @@
|
||||
<button class="primary" id="restart-btn">Neuen Ordner analysieren</button>
|
||||
</div>
|
||||
|
||||
<!-- Lightbox -->
|
||||
<div class="lightbox" id="lightbox">
|
||||
<span class="lightbox-close" id="lightbox-close">×</span>
|
||||
<img id="lightbox-img" src="" alt="">
|
||||
<span class="lightbox-name" id="lightbox-name"></span>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// --- State ---
|
||||
let analysisResults = [];
|
||||
let okPaths = [];
|
||||
let currentFolder = "";
|
||||
|
||||
// --- Lightbox ---
|
||||
function openLightbox(src, name) {
|
||||
el("lightbox-img").src = src;
|
||||
el("lightbox-name").textContent = name;
|
||||
el("lightbox").classList.add("open");
|
||||
}
|
||||
el("lightbox-close").addEventListener("click", () => el("lightbox").classList.remove("open"));
|
||||
el("lightbox").addEventListener("click", e => { if (e.target === el("lightbox")) el("lightbox").classList.remove("open"); });
|
||||
|
||||
// --- Helpers ---
|
||||
function showView(id) {
|
||||
document.querySelectorAll(".view").forEach(v => v.classList.remove("active"));
|
||||
@@ -207,6 +235,7 @@
|
||||
}
|
||||
|
||||
analysisResults = data.results;
|
||||
okPaths = data.ok_paths || [];
|
||||
|
||||
if (analysisResults.length === 0) {
|
||||
renderResult(0);
|
||||
@@ -222,10 +251,27 @@
|
||||
});
|
||||
|
||||
// --- Review ---
|
||||
function makeThumb(path, name) {
|
||||
const img = document.createElement("img");
|
||||
img.src = "/preview?path=" + encodeURIComponent(path);
|
||||
img.alt = name;
|
||||
img.onerror = function() { this.style.display = "none"; };
|
||||
img.addEventListener("click", () => openLightbox(img.src, name));
|
||||
return img;
|
||||
}
|
||||
|
||||
function renderReview() {
|
||||
const list = el("review-list");
|
||||
list.textContent = "";
|
||||
|
||||
// --- Aussortierte Fotos ---
|
||||
if (analysisResults.length > 0) {
|
||||
const div = document.createElement("div");
|
||||
div.className = "section-divider";
|
||||
div.textContent = "Aussortieren (" + analysisResults.length + ")";
|
||||
list.appendChild(div);
|
||||
}
|
||||
|
||||
analysisResults.forEach((item, idx) => {
|
||||
const name = item.path.split("/").pop();
|
||||
|
||||
@@ -233,11 +279,6 @@
|
||||
row.className = "photo-item";
|
||||
row.id = "item-" + idx;
|
||||
|
||||
const img = document.createElement("img");
|
||||
img.src = "/preview?path=" + encodeURIComponent(item.path);
|
||||
img.alt = name;
|
||||
img.onerror = function() { this.style.display = "none"; };
|
||||
|
||||
const info = document.createElement("div");
|
||||
info.className = "photo-info";
|
||||
|
||||
@@ -245,6 +286,11 @@
|
||||
nameEl.className = "photo-name";
|
||||
nameEl.textContent = name;
|
||||
|
||||
const badge = document.createElement("span");
|
||||
badge.className = "badge-reject";
|
||||
badge.textContent = "aussortieren";
|
||||
nameEl.appendChild(badge);
|
||||
|
||||
const reasonsEl = document.createElement("div");
|
||||
reasonsEl.className = "photo-reasons";
|
||||
reasonsEl.textContent = item.reasons.join(", ");
|
||||
@@ -257,14 +303,48 @@
|
||||
btn.textContent = "Behalten";
|
||||
btn.addEventListener("click", () => {
|
||||
row.classList.toggle("kept");
|
||||
btn.textContent = row.classList.contains("kept") ? "Aussortieren" : "Behalten";
|
||||
const isKept = row.classList.contains("kept");
|
||||
btn.textContent = isKept ? "Aussortieren" : "Behalten";
|
||||
badge.textContent = isKept ? "i.O." : "aussortieren";
|
||||
badge.className = isKept ? "badge-ok" : "badge-reject";
|
||||
});
|
||||
|
||||
row.appendChild(img);
|
||||
row.appendChild(makeThumb(item.path, name));
|
||||
row.appendChild(info);
|
||||
row.appendChild(btn);
|
||||
list.appendChild(row);
|
||||
});
|
||||
|
||||
// --- i.O. Fotos ---
|
||||
if (okPaths.length > 0) {
|
||||
const div = document.createElement("div");
|
||||
div.className = "section-divider";
|
||||
div.textContent = "Behalten – i.O. (" + okPaths.length + ")";
|
||||
list.appendChild(div);
|
||||
|
||||
okPaths.forEach(path => {
|
||||
const name = path.split("/").pop();
|
||||
const row = document.createElement("div");
|
||||
row.className = "photo-item";
|
||||
|
||||
const info = document.createElement("div");
|
||||
info.className = "photo-info";
|
||||
|
||||
const nameEl = document.createElement("div");
|
||||
nameEl.className = "photo-name";
|
||||
nameEl.textContent = name;
|
||||
|
||||
const badge = document.createElement("span");
|
||||
badge.className = "badge-ok";
|
||||
badge.textContent = "i.O.";
|
||||
nameEl.appendChild(badge);
|
||||
|
||||
info.appendChild(nameEl);
|
||||
row.appendChild(makeThumb(path, name));
|
||||
row.appendChild(info);
|
||||
list.appendChild(row);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// --- Confirm & Move ---
|
||||
@@ -323,7 +403,8 @@
|
||||
stats.appendChild(row);
|
||||
}
|
||||
|
||||
addStat("Analysierte Fotos", analysisResults.length);
|
||||
addStat("Analysierte Fotos", analysisResults.length + okPaths.length);
|
||||
addStat("Behalten (i.O.)", okPaths.length);
|
||||
addStat("Aussortiert", movedCount);
|
||||
Object.entries(byReason).forEach(([reason, count]) => {
|
||||
addStat(" davon: " + reason, count);
|
||||
|
||||
Reference in New Issue
Block a user