diff options
Diffstat (limited to 'views/admin/person_detail.erb')
| -rw-r--r-- | views/admin/person_detail.erb | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/views/admin/person_detail.erb b/views/admin/person_detail.erb new file mode 100644 index 0000000..448aaff --- /dev/null +++ b/views/admin/person_detail.erb @@ -0,0 +1,107 @@ +<div class="admin-people"> + <div class="admin-nav"> + <a href="/admin/people" class="btn btn-sm">← All Clusters</a> + <% if @name %> + <a href="/people/<%= ERB::Util.url_encode(@name.downcase.gsub(/[^a-z0-9]+/,'-').gsub(/^-+|-+$/,'')) %>" + class="btn btn-sm" target="_blank">Public Page ↗</a> + <% end %> + </div> + + <h1><%= @name ? ERB::Util.html_escape(@name) : 'Unnamed cluster' %></h1> + <p class="update-hint"><%= @count %> photo<%= @count == 1 ? '' : 's' %> in this cluster</p> + + <%# ── Name ────────────────────────────────────────────────────────────── %> + <section style="margin-bottom:20px"> + <form method="post" action="/admin/people/<%= ERB::Util.url_encode(@uuid) %>" class="name-form"> + <input type="text" name="name" value="<%= ERB::Util.html_escape(@name.to_s) %>" + placeholder="Enter name…" class="name-input" style="max-width:300px"> + <button type="submit" class="btn">Save Name</button> + </form> + </section> + + <%# ── Merge entire cluster ────────────────────────────────────────────── %> + <% unless @all_others.empty? %> + <section class="admin-update" style="margin-bottom:28px"> + <h2>Merge entire cluster into another person</h2> + <p class="update-hint">Moves all <%= @count %> photos and removes this cluster.</p> + <form method="post" action="/admin/people/<%= ERB::Util.url_encode(@uuid) %>/merge" + style="display:flex;gap:8px;align-items:center;flex-wrap:wrap"> + <select name="into" class="name-input" style="max-width:300px"> + <option value="">— Select person —</option> + <% @all_others.each do |p| %> + <option value="<%= ERB::Util.html_escape(p[:uuid]) %>"><%= ERB::Util.html_escape(p[:name]) %></option> + <% end %> + </select> + <button type="submit" class="btn" + onclick="return confirm('Merge all <%= @count %> photos into the selected person?')">Merge</button> + </form> + </section> + <% end %> + + <%# ── Face grid ────────────────────────────────────────────────────────── %> + <p class="update-hint"> + Hover a face to see the full photo. + Click to open in the album. + <% unless @named_others.empty? %>Use the drop-down to move a face to another person.<% end %> + </p> + + <div class="face-detail-grid"> + <% @members.each do |m| %> + <% rel = m['rel']; box = m['box'] %> + <% parts = rel.split('/'); fname = parts.last; dir_rel = parts[0..-2].join('/') %> + <% album_url = dir_rel.empty? ? '/browse/' : "/browse/#{ERB::Util.html_escape(dir_rel)}" %> + <div class="face-detail-card"> + <div class="face-detail-thumb" data-thumb="/thumb/<%= ERB::Util.html_escape(rel) %>"> + <a href="<%= album_url %>?photo=<%= ERB::Util.url_encode(fname) %>" target="_blank"> + <img src="/face/<%= ERB::Util.html_escape(rel) %>?box=<%= ERB::Util.html_escape(box.join(',')) %>" + width="100" height="100" loading="lazy"> + </a> + </div> + <% unless @named_others.empty? %> + <form method="post" action="/admin/people/<%= ERB::Util.url_encode(@uuid) %>/move" + class="face-move-form"> + <input type="hidden" name="rel" value="<%= ERB::Util.html_escape(rel) %>"> + <input type="hidden" name="box" value="<%= ERB::Util.html_escape(box.to_json) %>"> + <select name="to" class="face-move-select" + onchange="if(this.value) this.form.submit()"> + <option value="">Move to…</option> + <% @named_others.each do |p| %> + <option value="<%= ERB::Util.html_escape(p[:uuid]) %>"><%= ERB::Util.html_escape(p[:name]) %></option> + <% end %> + <option value="new">New person</option> + </select> + </form> + <% end %> + </div> + <% end %> + </div> +</div> + +<div id="face-hover-preview"><img alt=""></div> + +<script> +(function () { + const preview = document.getElementById('face-hover-preview'); + const pimg = preview.querySelector('img'); + + function reposition(e) { + const pw = 312, ph = 312; + let x = e.clientX + 18, y = e.clientY + 18; + if (x + pw > window.innerWidth) x = e.clientX - pw - 8; + if (y + ph > window.innerHeight) y = e.clientY - ph - 8; + preview.style.left = x + 'px'; + preview.style.top = y + 'px'; + } + + document.querySelectorAll('.face-detail-thumb').forEach(function (el) { + el.querySelector('img').style.cursor = 'zoom-in'; + el.addEventListener('mouseenter', function (e) { + pimg.src = el.dataset.thumb; + preview.style.display = 'block'; + reposition(e); + }); + el.addEventListener('mousemove', reposition); + el.addEventListener('mouseleave', function () { preview.style.display = 'none'; }); + }); +})(); +</script> |
