[object Object]

← back to Animals

yolo: Surface adoptable animals on pound/shelter detail pages

53b3de13ad5fae97ccdc0021cfbb6a4bfc827ae8 · 2026-05-08 01:32:14 -0700 · animal-yolo

Task: yolo.pound-detail-photos
Prompt: In ~/Projects/animals: on /clinic/:id pages where category in ('shelter','rescue','pound'), query shelter_animals table for that business_id and show available animals as cards (name, breed, photo if

Files touched

Diff

commit 53b3de13ad5fae97ccdc0021cfbb6a4bfc827ae8
Author: animal-yolo <steve@designerwallcoverings.com>
Date:   Fri May 8 01:32:14 2026 -0700

    yolo: Surface adoptable animals on pound/shelter detail pages
    
    Task: yolo.pound-detail-photos
    Prompt: In ~/Projects/animals: on /clinic/:id pages where category in ('shelter','rescue','pound'), query shelter_animals table for that business_id and show available animals as cards (name, breed, photo if
---
 agents/animal-yolo/state.json |  4 ++--
 src/server/index.js           |  9 ++++++++-
 src/server/render.js          | 40 +++++++++++++++++++++++++++++++++++++++-
 3 files changed, 49 insertions(+), 4 deletions(-)

diff --git a/agents/animal-yolo/state.json b/agents/animal-yolo/state.json
index a1cbf4b..1b17582 100644
--- a/agents/animal-yolo/state.json
+++ b/agents/animal-yolo/state.json
@@ -1,5 +1,5 @@
 {
-  "cursor": 1,
-  "runs": 1,
+  "cursor": 2,
+  "runs": 2,
   "started_at": "2026-05-08T07:01:22.386Z"
 }
\ No newline at end of file
diff --git a/src/server/index.js b/src/server/index.js
index 84d112e..0c65fd6 100644
--- a/src/server/index.js
+++ b/src/server/index.js
@@ -934,7 +934,14 @@ app.get('/clinic/:id', async (req, res) => {
   if (!biz) return res.status(404).send(renderNotFound('Listing not found'));
   const audit = await one('SELECT * FROM site_audits WHERE business_id=$1 ORDER BY audited_at DESC LIMIT 1', [biz.id]);
   const ad = await pickAdForPage({ pagePath: req.path, keywords: [biz.category, biz.city], category: biz.category });
-  res.send(renderBusiness({ biz, audit, ad }));
+  const animals = ['shelter','rescue','pound'].includes(biz.category)
+    ? await many(`SELECT id, name, breed_primary, breed_secondary, age_class, photo_urls, source_url
+                  FROM shelter_animals
+                  WHERE business_id=$1 AND status='available'
+                  ORDER BY fetched_at DESC
+                  LIMIT 60`, [biz.id])
+    : [];
+  res.send(renderBusiness({ biz, audit, ad, animals }));
 });
 
 // Owner pets gallery — /my-pets is a PRIVATE dashboard for the logged-in user.
diff --git a/src/server/render.js b/src/server/render.js
index bbae193..3131979 100644
--- a/src/server/render.js
+++ b/src/server/render.js
@@ -635,7 +635,44 @@ export function renderCategory({ category, dbCategory, businesses, ad }) {
 </script>` + footer() + '</body></html>';
 }
 
-export function renderBusiness({ biz, audit, ad }) {
+export function renderBusiness({ biz, audit, ad, animals = [] }) {
+  const animalsBlock = animals.length ? `
+    <section class="adoptable">
+      <h2>Adoptable animals <span class="muted small">(${animals.length})</span></h2>
+      <div class="adoptable-grid">
+        ${animals.map(a => {
+          const photo = Array.isArray(a.photo_urls) && a.photo_urls[0] ? a.photo_urls[0] : '';
+          const breed = [a.breed_primary, a.breed_secondary].filter(Boolean).join(' / ');
+          const card = `
+            <article class="adoptable-card">
+              ${photo
+                ? `<img src="${esc(photo)}" alt="${esc(a.name || 'Adoptable animal')}" loading="lazy">`
+                : `<div class="adoptable-noimg">🐾</div>`}
+              <div class="adoptable-meta">
+                <strong>${esc(a.name || 'Unnamed')}</strong>
+                ${breed ? `<span class="muted small">${esc(breed)}</span>` : ''}
+                ${a.age_class ? `<span class="age-tag">${esc(a.age_class)}</span>` : ''}
+              </div>
+            </article>`;
+          return a.source_url
+            ? `<a href="${esc(a.source_url)}" rel="nofollow noopener" target="_blank" class="adoptable-link">${card}</a>`
+            : card;
+        }).join('')}
+      </div>
+    </section>
+    <style>
+      .adoptable{margin:32px 0}
+      .adoptable h2{font-weight:400;margin-bottom:14px}
+      .adoptable-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:14px}
+      .adoptable-card{background:#fff;border:1px solid #d2dac8;border-radius:10px;overflow:hidden;display:flex;flex-direction:column}
+      .adoptable-card img{width:100%;aspect-ratio:1;object-fit:cover;background:#eef2eb;display:block}
+      .adoptable-noimg{width:100%;aspect-ratio:1;background:#eef2eb;display:flex;align-items:center;justify-content:center;font-size:42px;color:#9aa89a}
+      .adoptable-meta{padding:10px 12px;display:flex;flex-direction:column;gap:3px;font-size:13px}
+      .adoptable-meta strong{font-weight:500;color:#0f4d3a}
+      .age-tag{align-self:flex-start;background:#fdf5e6;border:1px solid #d4a04a;color:#5a4020;padding:1px 7px;border-radius:4px;font-size:11px;text-transform:capitalize;margin-top:2px}
+      .adoptable-link{text-decoration:none;color:inherit}
+      .adoptable-link:hover .adoptable-card{box-shadow:0 2px 8px rgba(15,77,58,.12)}
+    </style>` : '';
   const auditBlock = audit ? `
     <aside class="audit">
       <h3>Website health: ${audit.marketing_score ?? '—'}/100</h3>
@@ -673,6 +710,7 @@ export function renderBusiness({ biz, audit, ad }) {
       ${adSlot(ad, 'sidebar', `/clinic/${biz.id}`)}
     </div>
   </div>
+  ${animalsBlock}
   ${leadForm()}
 </main>` + footer() + '</body></html>';
 }

← f22df9f rel=alternate JSON + HTTP Link header on /breeds and /photog  ·  back to Animals  ·  yolo: Breed page: nearest businesses that work with this bre e7e8d06 →