admin: no avatar on the detail page without a picture
Assisted-by: GLM-5.3 via pi
change upxvyxvuqmprukswtpmnlkmpyxskytpo
commit a58d245223fc90ede690c60982c7e321910a7872
author Alpha Chen <alpha@kejadlen.dev>
date
parent qpzruxyp
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: