From cfb814470864785565f33e4bebd2aca7e67c16ae Mon Sep 17 00:00:00 2001 From: Ken D'Ambrosio Date: Fri, 12 Jun 2026 14:01:48 +0000 Subject: Move bulk selection to admin cluster detail page (correct page) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bulk selection panel now lives on /admin/people/:uuid — the face crop grid page — which is what was actually requested. A sticky left panel shows the cluster name, the name form, a selection counter, and bulk action controls. Clicking a face crop toggles selection; clicking the photo link still opens the album. Bulk actions: move selected faces to a named person, move to pool, or blacklist. The per-face individual dropdowns are replaced by the panel. Merge-entire-cluster and Blacklist-cluster moved to collapsible/button in the panel too. Co-Authored-By: Claude Sonnet 4.6 --- app.rb | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) (limited to 'app.rb') diff --git a/app.rb b/app.rb index ece8b18..9f02422 100644 --- a/app.rb +++ b/app.rb @@ -1007,6 +1007,46 @@ post '/admin/people/:uuid/delete' do redirect '/admin/people' end +post '/admin/people/:uuid/bulk_move' do + require_admin! + src = params[:uuid] + action = params['bulk_action'].to_s.strip + entries = Array(params['entries[]']).map { |e| JSON.parse(e) rescue nil }.compact + + halt 400 if entries.empty? + + data = load_people_data + people = data['people'] || {} + halt 404 unless people.key?(src) + + to_move = entries.filter_map do |e| + rel = e['rel']; box = e['box'].map(&:to_i) + people[src]['members'].find { |m| m['rel'] == rel && m['box'].map(&:to_i) == box } + end + + people[src]['members'] -= to_move + + case action + when 'blacklist' + data['blacklist'] ||= [] + data['blacklist'].concat(to_move) + when 'pool' + people['__pool__'] ||= { 'name' => '__pool__', 'slug' => nil, 'members' => [] } + people['__pool__']['members'].concat(to_move) + when /\A[0-9a-f-]{36}\z/ + halt 404 unless people.key?(action) + people[action]['members'].concat(to_move) + else + halt 400 + end + + people.delete(src) if 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/merge' do require_admin! src = params[:uuid] -- cgit v1.2.3