summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKen D'Ambrosio <ken@jots.org>2026-06-08 22:39:58 +0000
committerKen D'Ambrosio <ken@jots.org>2026-06-08 22:39:58 +0000
commitcf1385bbd6d88a8db9f615512564e150c85a0b5f (patch)
tree371bc4b8aea8dd6f4fa2771c12a68ce7fca14eae
parent7f6325fe213ed46ff5479ffd34b0e212426d48f2 (diff)
Add face and cluster deletion to people admin
On the cluster detail page: "Remove face" option in each face's move dropdown removes it from the cluster entirely; "Delete cluster" button (red, with confirmation) removes the whole cluster from people.json. Moving the last face out of a cluster also auto-deletes it. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
-rw-r--r--app.rb15
-rw-r--r--public/css/style.css2
-rw-r--r--views/admin/person_detail.erb9
3 files changed, 25 insertions, 1 deletions
diff --git a/app.rb b/app.rb
index 7ce9d22..192814a 100644
--- a/app.rb
+++ b/app.rb
@@ -939,7 +939,9 @@ post '/admin/people/:uuid/move' do
halt 404 unless member
people[src]['members'].delete(member)
- if to == 'new'
+ if to == 'remove'
+ # just drop it — already deleted from src above
+ elsif to == 'new'
new_uid = SecureRandom.uuid
people[new_uid] = { 'name' => nil, 'slug' => nil, 'members' => [member] }
else
@@ -947,12 +949,23 @@ post '/admin/people/:uuid/move' do
people[to]['members'] << member
end
+ people.delete(src) if people[src] && people[src]['members'].empty?
data['people'] = people
atomic_write(PEOPLE_PATH, JSON.pretty_generate(data))
people[src] ? redirect("/admin/people/#{src}") : redirect('/admin/people')
end
+post '/admin/people/:uuid/delete' do
+ require_admin!
+ data = load_people_data
+ people = data['people'] || {}
+ people.delete(params[:uuid])
+ data['people'] = people
+ atomic_write(PEOPLE_PATH, JSON.pretty_generate(data))
+ redirect '/admin/people'
+end
+
post '/admin/people/:uuid/merge' do
require_admin!
src = params[:uuid]
diff --git a/public/css/style.css b/public/css/style.css
index 7b0ce1a..64c19e3 100644
--- a/public/css/style.css
+++ b/public/css/style.css
@@ -66,6 +66,8 @@ a:hover { color: var(--accent-hv); text-decoration: underline; }
transition: background .15s;
}
.btn:hover { background: var(--accent); border-color: var(--accent); color: #fff; text-decoration: none; }
+.btn-danger { border-color: #8b2020; color: #e07070; }
+.btn-danger:hover { background: #8b2020; border-color: #8b2020; }
.btn-sm { padding: 4px 10px; font-size: .8rem; }
/* ── Main content ──────────────────────────────────────────────────────── */
diff --git a/views/admin/person_detail.erb b/views/admin/person_detail.erb
index 448aaff..8a2f8fe 100644
--- a/views/admin/person_detail.erb
+++ b/views/admin/person_detail.erb
@@ -38,6 +38,14 @@
</section>
<% end %>
+ <%# ── Delete cluster ──────────────────────────────────────────────────── %>
+ <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>
+ </form>
+ </section>
+
<%# ── Face grid ────────────────────────────────────────────────────────── %>
<p class="update-hint">
Hover a face to see the full photo.
@@ -69,6 +77,7 @@
<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>
</select>
</form>
<% end %>