admin: no avatar on the detail page without a picture
Assisted-by: GLM-5.3 via pi
diff --git a/lib/pro_tacts/admin/avatar.rb b/lib/pro_tacts/admin/avatar.rb
index 64310e9..569cddf 100644
--- a/lib/pro_tacts/admin/avatar.rb
+++ b/lib/pro_tacts/admin/avatar.rb
@@ -6,9 +6,10 @@ module ProTacts
module Admin
# A contact's avatar: the card's picture when it carries one a
# browser can show, the initials otherwise. One component because
- # three views render this slot — the dashboard's rows, the
- # birthdays' rows, the detail header — and they should not disagree
- # about what an avatar is.
+ # the views that render this slot — the dashboard's rows, the
+ # birthdays' rows, the detail header — should not disagree about
+ # what an avatar is. (The detail header asks for it only when a
+ # picture exists; see ContactsShow.)
#
# The picture renders from the photo route rather than the decoded
# bytes in hand, so the page carries a URL where it would otherwise
diff --git a/lib/pro_tacts/admin/contacts_show.rb b/lib/pro_tacts/admin/contacts_show.rb
index a3eff22..dfa1ae5 100644
--- a/lib/pro_tacts/admin/contacts_show.rb
+++ b/lib/pro_tacts/admin/contacts_show.rb
@@ -30,7 +30,11 @@ module ProTacts
div(class: "card-body") do
div(class: "detail-header") do
h1(class: "type-h2", style: "margin: 0;") { @contact.name || @contact.id }
- render Avatar.new(contact: @contact, size: "xl")
+ # Only a picture earns this slot: an initials circle
+ # beside the name in type-h2 would repeat what the name
+ # already says. The dashboard rows keep theirs — there
+ # the avatar is the row's visual anchor, not a caption.
+ render Avatar.new(contact: @contact, size: "xl") if @contact.photo
end
# Only rendered when there's something to show: an empty
# <dl> would still take up the gap card-body puts between
diff --git a/test/pro_tacts/admin/test_contacts_pages.rb b/test/pro_tacts/admin/test_contacts_pages.rb
index 23b2dce..dd65688 100644
--- a/test/pro_tacts/admin/test_contacts_pages.rb
+++ b/test/pro_tacts/admin/test_contacts_pages.rb
@@ -265,6 +265,18 @@ class AdminContactsPagesTest < Minitest::Test
## Photos
+ # No picture, no avatar: an initials circle beside the name in
+ # type-h2 would repeat what the name already says, so a contact
+ # without a photo renders the header as the name alone.
+ def test_show_without_a_picture_renders_no_avatar
+ with_contacts({"ada" => ADA}) do
+ get "/contacts/ada"
+
+ assert_equal 200, last_response.status
+ refute_includes last_response.body, 'class="avatar"'
+ end
+ end
+
# A contact with a picture renders it where the initials were —
# here the detail header's large avatar — pointing at the photo
# route rather than carrying the bytes in the page. Alt is empty: