# 2026-03-07 (Saturday)

## Workspace Backup
- First full backup uploaded to Supabase Storage (42MB critical backup)
- Signed URL valid 7 days
- Excluded client repos (1.8GB) and node_modules -- can be rebuilt
- Cloudflare R2 not enabled on account; used Supabase Storage instead

## Retail Targeting Chrome Extension - Major Bug Fix Session

Rob spent 2+ hours testing the extension hands-on. Multiple rounds of fixes:

### Bugs Found & Fixed
1. **Mock data everywhere** -- mock.ts was imported in Dashboard, Settings, AdCreative, UploadProducts, SetBudget. App looked like it worked but was entirely fake data. **DELETED mock.ts entirely.** All pages now use real Facebook API, Gemini AI, and chrome.storage.

2. **Store data not persisting** -- `handleUpload` in UploadProducts called `api.uploadProducts()` (mock) but never saved to `chrome.storage.local`. Budget page loaded from storage and found nothing → "No stores match." Fixed: save `store_data` to chrome.storage before navigating.

3. **White screen on launch** -- `calculateBudgetAllocation()` was called during render (in JSX) but called `setBudgetValidationResult()` and `setShowBudgetWarning()` inside it. State updates during render = infinite loop → crash. Fixed: made function pure, no state updates.

4. **Column mapping wrong** -- `store_name` pattern was in both `store` and `location` possible matches. `location` (REQUIRED_COLUMNS) was iterated first, grabbed `Store_Name` before `store` could. Fixed: store maps first, columns can't double-map (usedCsvColumns Set), location derives from store name.

5. **Store names showing raw CSV caps** -- "ARTAINE" instead of "Tesco Artaine". Fixed: auto-prepend retailer name + title case when store name doesn't already have retailer prefix.

6. **"No stores match" as browser alert** -- Rob specifically requested: no browser alerts, show inline options within the app. Fixed: inline card with two options ("Spread budget across all stores" / "Increase threshold").

7. **Facebook Page missing** -- Campaign creation requires a Facebook Page (ads appear as Page posts). Was showing as browser alert. Fixed: inline red warning card with link to create a Page, plus page selector dropdown if multiple pages exist.

8. **Gemini 429 rate limiting** -- Free tier exhausted. Added retry with longer backoff (5s/15s/30s). Changed error from scary red to friendly blue: "Your uploaded image will work great."

9. **Upload button stuck loading** -- `setUploading(true)` never reset on error. Fixed: try/catch/finally.

10. **Scroll jumping to bottom** -- Added `ScrollToTop` component that scrolls to top on every route change.

### Key Lesson
**Rob said: "should the claude code CLI not have found all these errors in testing?"** -- He's right. Mock data masked every bug. The app looked functional but was entirely fake. Going forward: test against real APIs, never mock data, and run QA before sending builds.

### Extension IDs Seen Today
- mocbfnplbfhmkaaacmiepddlalcfbhli (original)
- obofkjnhkahocmckilbicecngojibdip
- fhbhiaommmobhbjkfjcgeodckhbjehgl (seen twice - same folder path)
- ilkonpnooblkfmbkaadcmcpnlficilag (seen twice)

Tip given to Rob: keep extension files in one fixed folder to avoid ID changes.

11. **Min budget per store** -- Rob requested €10 minimum per ad set (was €5). Updated MIN_BUDGET_PER_ADSET from 500 to 1000 cents and all UI references.

### Current Status
- Extension builds clean, zero mock imports
- Facebook OAuth working
- Store data persists through flow
- Budget allocation working (screenshot showed real Tesco stores with budget splits)
- Store names now show as "Tesco Artaine" etc. with proper title case
- Inline options when no stores match criteria (no browser alerts)
- Facebook Page selector/warning shown inline
- Min budget per store: €10
- **Blocker:** Rob needs a Facebook Page connected to his ad account to create campaigns
- AI image generation works but Gemini free tier rate limits are tight

### Rob's Mood
Frustrated. "zzzzz" and "im tired of these tests." Fair enough -- he shouldn't be doing QA. Need to be much more thorough before sending builds.

## Projects Status Snapshot
- **Retail Targeting** -- Chrome extension working (Facebook OAuth live), UI rebuilt, deployed to retail-targeting.pages.dev
- **Tradeify** -- Feature-complete, security patched, pre-launch blocked by Rob-gated items
- **Irelandify** -- Live, needs CMS + content
- **Halo Effect Dashboard** -- Live at halo-glow-dashboard.lovable.app

## Tradeify UI Fixes (Evening Session ~19:45-21:20)

### Enterprise Tier Sorting Fixed
- tierSortWeight function was correct (enterprise=0, pro=1, free=2) but tierData was loaded async via getTierForTradesperson() -- race condition meant sort ran before tier data arrived
- Fix: read subscription_tier directly from the person object (already in SELECT * result) instead of separate async calls per person
- Removed getTierForTradesperson import from Search.tsx entirely
- Added getTier() helper that checks person.subscription_tier first, falls back to tierData state

### Enterprise/Pro Badge Placement
- Rob wanted badge on third line (reviews/unclaimed line), not next to company name
- Final layout: Line 1 = name + verified tick, Line 2 = trade + distance, Line 3 = reviews/unclaimed + tier badge
- Removed "yet" from "No reviews yet" to fit badge better

### Post Job 404 Fix
- Post Job form navigated to /my-jobs after submission but hit 404
- Root cause: Cloudflare Pages with Functions intercepts ALL routes by default
- Fix: Added public/_routes.json with include:["/api/*"] so only API routes go to Functions, everything else serves SPA

### Quote Requests Not Showing on Trade Dashboard
- Profile page "Request a Quote" saves to quote_requests table
- Trade dashboard Leads tab only read from jobs table (Post Job flow)
- Two completely separate systems that weren't connected
- Fix: Added quote_requests fetch to TradesDashboard loadData(), displayed as "Direct" leads with green badge above job leads
- Rob's test quote ("Need a full house rewire" for Mahony Group) was in DB but invisible on dashboard

### Message Bubbles Too Small + Scroll Bug
- Bubbles had minWidth:60px, padding:8px 14px, fontSize:15px -- too cramped
- Fixed to minWidth:120px, padding:10px 16px, fontSize:16px, maxWidth:80%
- Scroll bug: scrollIntoView fired before DOM rendered new message, so sent message wasn't visible
- Fix: 50ms setTimeout before scrollIntoView to let React render first
- Input textarea slightly enlarged (minHeight 52px, text-base class)

### Test Listings to Clean Up
- "general" with question mark avatar -- no name, no trade, likely test data
- "Wash Masters" -- Enterprise tier with verified tick, unclear if real or test
- Rob didn't respond to cleanup question yet
