[object Object]

← back to Ventura Claw Leads

yolo tick 24: home featured grid — photo-priority within tier + render hero per card

e1590f826a02ebb4270344f3b42834df0f95387c · 2026-05-07 00:58:51 -0700 · Steve Abrams

routes/public.js GET /: featured query expanded to SELECT photo_path, plus
new ORDER BY tier within priority that prefers businesses-with-photos to
businesses-without-photos within the same tier:

  ORDER BY tier='premier' DESC, tier='standard' DESC,
           (photo_path IS NOT NULL) DESC,
           RANDOM()

So premier listings still always sort first; standard next; within each
tier the rotation favors visually-richer cards that have a real photo
uploaded. Pure positive incentive to upload (better placement) without
penalizing photo-less listings (they still appear, just lower in the
random pool).

views/public/home.ejs featured grid: same per-card hero treatment as /find
— photo when present, vertical-themed gradient + emoji glyph as fallback.
Featured cards now visually match the directory's card pattern.

46/46 tests still pass. The home page transforms once businesses upload
photos: the gradient placeholders give way to actual interior shots,
storefronts, etc.

Files touched

Diff

commit e1590f826a02ebb4270344f3b42834df0f95387c
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu May 7 00:58:51 2026 -0700

    yolo tick 24: home featured grid — photo-priority within tier + render hero per card
    
    routes/public.js GET /: featured query expanded to SELECT photo_path, plus
    new ORDER BY tier within priority that prefers businesses-with-photos to
    businesses-without-photos within the same tier:
    
      ORDER BY tier='premier' DESC, tier='standard' DESC,
               (photo_path IS NOT NULL) DESC,
               RANDOM()
    
    So premier listings still always sort first; standard next; within each
    tier the rotation favors visually-richer cards that have a real photo
    uploaded. Pure positive incentive to upload (better placement) without
    penalizing photo-less listings (they still appear, just lower in the
    random pool).
    
    views/public/home.ejs featured grid: same per-card hero treatment as /find
    — photo when present, vertical-themed gradient + emoji glyph as fallback.
    Featured cards now visually match the directory's card pattern.
    
    46/46 tests still pass. The home page transforms once businesses upload
    photos: the gradient placeholders give way to actual interior shots,
    storefronts, etc.
---
 routes/public.js      |  6 ++++--
 views/public/home.ejs | 15 +++++++++++++--
 2 files changed, 17 insertions(+), 4 deletions(-)

diff --git a/routes/public.js b/routes/public.js
index c8d2a1d..4e8646e 100644
--- a/routes/public.js
+++ b/routes/public.js
@@ -17,10 +17,12 @@ router.get('/', async (req, res, next) => {
         FROM businesses WHERE status = 'active'
     `);
     const featured = await db.many(`
-      SELECT slug, business_name, vertical, neighborhood, city, headline
+      SELECT slug, business_name, vertical, neighborhood, city, headline, photo_path
         FROM businesses
        WHERE status = 'active' AND (claim_status IN ('self','claimed') OR tier != 'free')
-       ORDER BY tier = 'premier' DESC, tier = 'standard' DESC, RANDOM()
+       ORDER BY tier = 'premier' DESC, tier = 'standard' DESC,
+                (photo_path IS NOT NULL) DESC,   -- within tier, photos beat no-photos
+                RANDOM()
        LIMIT 9
     `);
     const verticalCounts = await db.many(`
diff --git a/views/public/home.ejs b/views/public/home.ejs
index e5685dc..f72e818 100644
--- a/views/public/home.ejs
+++ b/views/public/home.ejs
@@ -87,9 +87,20 @@
 <section class="find-page">
   <h2>Featured this week</h2>
   <div class="business-grid">
-    <% featured.forEach(function(b){ %>
+    <% featured.forEach(function(b){
+         var vMeta = verticals.find(function(v){return v.key===b.vertical});
+    %>
       <a class="business-card" href="/business/<%= b.slug %>">
-        <p class="biz-vertical"><%= verticals.find(function(v){return v.key===b.vertical}) ? verticals.find(function(v){return v.key===b.vertical}).label : b.vertical %></p>
+        <% if (b.photo_path) { %>
+          <div class="biz-card-hero biz-card-hero-photo">
+            <img src="<%= b.photo_path %>" alt="<%= b.business_name %>" loading="lazy" decoding="async">
+          </div>
+        <% } else { %>
+          <div class="biz-card-hero vertical-hero-<%= b.vertical %>" aria-hidden="true">
+            <span class="biz-card-glyph"><%= vMeta ? vMeta.icon : '•' %></span>
+          </div>
+        <% } %>
+        <p class="biz-vertical"><%= vMeta ? vMeta.label : b.vertical %></p>
         <p class="biz-name"><%= b.business_name %></p>
         <p class="biz-loc"><%= [b.neighborhood, b.city, b.state].filter(Boolean).join(' · ') %></p>
         <% if (b.headline) { %><p class="biz-headline"><%= b.headline %></p><% } %>

← c73de6a yolo tick 23: og:image + Twitter Card image + 'Share this li  ·  back to Ventura Claw Leads  ·  yolo tick 25: /find auto-suggest dropdown — debounced AJAX, 870584a →