diff options
| author | Ken D'Ambrosio <ken@jots.org> | 2026-06-09 13:09:23 +0000 |
|---|---|---|
| committer | Ken D'Ambrosio <ken@jots.org> | 2026-06-09 13:09:23 +0000 |
| commit | c13a40a970be156a231200c20362636b198d32ec (patch) | |
| tree | 9e7c0103d861bbd4dd5877d244431ca5ae07327f /views/admin | |
| parent | cf1385bbd6d88a8db9f615512564e150c85a0b5f (diff) | |
Add face pool, blacklisting, and action explanations to people admin
Removed faces now go to an "Unidentified pool" cluster rather than
disappearing. Deleting a cluster blacklists all its members so they are
skipped by future re-clustering runs. Pool faces can be assigned to a
named person or individually blacklisted. A plain-English info box on
the detail page explains what each action does and that no photo files
are ever modified.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'views/admin')
| -rw-r--r-- | views/admin/people.erb | 20 | ||||
| -rw-r--r-- | views/admin/person_detail.erb | 59 |
2 files changed, 64 insertions, 15 deletions
diff --git a/views/admin/people.erb b/views/admin/people.erb index c17e847..9715b95 100644 --- a/views/admin/people.erb +++ b/views/admin/people.erb @@ -26,6 +26,26 @@ </div> </section> + <% if @pool %> + <div class="people-pool-row"> + <div class="face-samples"> + <% @pool[:samples].each do |s| %> + <img src="/face/<%= ERB::Util.html_escape(s[:rel]) %>?box=<%= ERB::Util.html_escape(s[:box].join(',')) %>" + width="72" height="72" loading="lazy" + class="face-list-thumb" + data-thumb="/thumb/<%= ERB::Util.html_escape(s[:rel]) %>"> + <% end %> + <% if @pool[:count] > @pool[:samples].length %> + <a href="/admin/people/__pool__" class="face-more">+<%= @pool[:count] - @pool[:samples].length %> more</a> + <% end %> + </div> + <div class="people-admin-meta"> + <span class="face-count pool-label">Unidentified pool — <%= @pool[:count] %> face<%= @pool[:count] == 1 ? '' : 's' %></span> + <a href="/admin/people/__pool__" class="btn btn-sm">Review</a> + </div> + </div> + <% end %> + <% if @clusters.empty? %> <p class="empty-album" style="margin-top: 40px"> No face data yet — the face daemon is still processing, or run Re-cluster once it has finished a pass. diff --git a/views/admin/person_detail.erb b/views/admin/person_detail.erb index 8a2f8fe..77f27b7 100644 --- a/views/admin/person_detail.erb +++ b/views/admin/person_detail.erb @@ -1,16 +1,30 @@ <div class="admin-people"> <div class="admin-nav"> <a href="/admin/people" class="btn btn-sm">← All Clusters</a> - <% if @name %> + <% if @name && !@is_pool %> <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> + <h1><%= ERB::Util.html_escape(@title) %></h1> <p class="update-hint"><%= @count %> photo<%= @count == 1 ? '' : 's' %> in this cluster</p> - <%# ── Name ────────────────────────────────────────────────────────────── %> + <div class="people-info-box"> + <% if @is_pool %> + These faces were removed from other clusters and are waiting to be assigned. + Use <strong>Assign to…</strong> to move a face to a named person, or + <strong>Blacklist</strong> to permanently exclude it from future clustering. + <% else %> + <strong>Move to pool</strong> sends a face to a holding area where you can assign it later. + <strong>Blacklist cluster</strong> permanently excludes all faces in this cluster from future clustering. + <br> + <em>No photo files are ever modified — only the clustering metadata is affected.</em> + <% end %> + </div> + + <% unless @is_pool %> + <%# ── 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) %>" @@ -19,7 +33,7 @@ </form> </section> - <%# ── Merge entire cluster ────────────────────────────────────────────── %> + <%# ── Merge entire cluster ──────────────────────────────────────────────── %> <% unless @all_others.empty? %> <section class="admin-update" style="margin-bottom:28px"> <h2>Merge entire cluster into another person</h2> @@ -38,19 +52,31 @@ </section> <% end %> - <%# ── Delete cluster ──────────────────────────────────────────────────── %> + <%# ── Delete cluster (blacklists all members) ───────────────────────────── %> <section style="margin-bottom:28px"> <form method="post" action="/admin/people/<%= ERB::Util.url_encode(@uuid) %>/delete" - onsubmit="return confirm('Delete this entire cluster (<%= @count %> photo<%= @count == 1 ? '' : 's' %>)? This cannot be undone.')"> - <button type="submit" class="btn btn-danger">Delete cluster</button> + onsubmit="return confirm('Blacklist this entire cluster (<%= @count %> photo<%= @count == 1 ? '' : 's' %>)? These faces will never be re-clustered.')"> + <button type="submit" class="btn btn-danger">Blacklist cluster</button> </form> </section> + <% else %> + <%# ── Pool: blacklist all ────────────────────────────────────────────────── %> + <section style="margin-bottom:28px"> + <form method="post" action="/admin/people/__pool__/blacklist_all" + onsubmit="return confirm('Blacklist all <%= @count %> faces in the pool? They will never be re-clustered.')"> + <button type="submit" class="btn btn-danger">Blacklist all in pool</button> + </form> + </section> + <% end %> - <%# ── Face grid ────────────────────────────────────────────────────────── %> + <%# ── 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 %> + Hover a face to see the full photo. Click to open in the album. + <% if @is_pool %> + Use the drop-down to assign a face to a named person, or blacklist it permanently. + <% elsif !@named_others.empty? %> + Use the drop-down to move a face to another person. + <% end %> </p> <div class="face-detail-grid"> @@ -65,19 +91,22 @@ width="100" height="100" loading="lazy"> </a> </div> - <% unless @named_others.empty? %> + <% if @is_pool || !@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> + <option value=""><%= @is_pool ? 'Assign to…' : '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> - <option value="remove">Remove face</option> + <% unless @is_pool %> + <option value="new">New person</option> + <option value="remove">Move to pool</option> + <% end %> + <option value="blacklist">Blacklist</option> </select> </form> <% end %> |
