# High Opportunity Keywords for Add Keywords Page **Plan Date:** January 14, 2026 **Target Page:** `/setup/add-keywords` ([IndustriesSectorsKeywords.tsx](../../frontend/src/pages/Setup/IndustriesSectorsKeywords.tsx)) **Reference Implementation:** Step 4 of Wizard ([Step4AddKeywords.tsx](../../frontend/src/components/onboarding/steps/Step4AddKeywords.tsx)) ## Overview Add a "High Opportunity Keywords" section to the top of the `/setup/add-keywords` page, similar to the wizard's Step 4 interface. This will allow users to quickly add curated keyword sets (Top 50 High Volume & Top 50 Low Difficulty) for each of their site's sectors. ## Current State ### Current Page Structure - **Page:** `IndustriesSectorsKeywords.tsx` (854 lines) - **Route:** `/setup/add-keywords` - **Primary Function:** Browse and search global seed keywords with filters, sorting, and pagination - **Current Features:** - Search by keyword text - Filter by country, difficulty, status (not added only) - Sort by keyword, volume, difficulty, country - Bulk selection and "Add Selected to Workflow" - Individual "Add to Workflow" buttons - Shows stats: X added / Y available - Admin CSV import functionality - Table-based display with pagination - Requires active sector selection to add keywords ### Wizard Step 4 Implementation - **Component:** `Step4AddKeywords.tsx` (738 lines) - **Features to Replicate:** - Groups keywords by sector (e.g., "Physiotherapy & Rehabilitation", "Relaxation Devices", "Massage & Therapy") - Two options per sector: - **Top 50 High Volume** - Keywords sorted by highest volume - **Top 50 Low Difficulty** - Keywords sorted by lowest difficulty (KD) - Shows keyword count and sample keywords (first 3 with "+X more" badge) - "Add All" button for each option - Visual feedback when keywords are added (success badge, green styling) - Loads seed keywords from API filtered by sector ## Proposed Implementation ### UI Design & Layout ``` ┌─────────────────────────────────────────────────────────────────┐ │ Page Header: "Find Keywords" │ └─────────────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────────┐ │ Back to options (link) │ │ │ │ High Opportunity Keywords (h2) │ │ Add top keywords for each of your sectors. Keywords will be │ │ added to your planner workflow. │ │ │ │ ┌─────────────┬─────────────┬─────────────┐ │ │ │ Sector 1 │ Sector 2 │ Sector 3 │ (3 columns) │ │ ├─────────────┼─────────────┼─────────────┤ │ │ │ ┌─────────┐ │ ┌─────────┐ │ ┌─────────┐ │ │ │ │ │Top 50 HV│ │ │Top 50 HV│ │ │Top 50 HV│ │ │ │ │ │X keywords│ │ │X keywords│ │ │X keywords│ │ │ │ │ │[kw] [kw]│ │ │[kw] [kw]│ │ │[kw] [kw]│ │ │ │ │ │+X more │ │ │+X more │ │ │+X more │ │ │ │ │ │[Add All]│ │ │[Add All]│ │ │[Add All]│ │ │ │ │ └─────────┘ │ └─────────┘ │ └─────────┘ │ │ │ │ ┌─────────┐ │ ┌─────────┐ │ ┌─────────┐ │ │ │ │ │Top 50 LD│ │ │Top 50 LD│ │ │Top 50 LD│ │ │ │ │ │X keywords│ │ │X keywords│ │ │X keywords│ │ │ │ │ │[kw] [kw]│ │ │[kw] [kw]│ │ │[kw] [kw]│ │ │ │ │ │+X more │ │ │+X more │ │ │+X more │ │ │ │ │ │[Add All]│ │ │[Add All]│ │ │[Add All]│ │ │ │ │ └─────────┘ │ └─────────┘ │ └─────────┘ │ │ │ └─────────────┴─────────────┴─────────────┘ │ │ │ │ ✓ X keywords added to your workflow │ └─────────────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────────┐ │ Browse All Keywords (existing table interface) │ │ [Search] [Filters] [Table] [Pagination] │ └─────────────────────────────────────────────────────────────────┘ ``` ### Component Architecture #### 1. New State Management Add to `IndustriesSectorsKeywords.tsx`: ```typescript // High Opportunity Keywords state const [showHighOpportunity, setShowHighOpportunity] = useState(true); const [loadingOpportunityKeywords, setLoadingOpportunityKeywords] = useState(false); const [sectorKeywordData, setSectorKeywordData] = useState([]); const [addingOption, setAddingOption] = useState(null); interface SectorKeywordOption { type: 'high-volume' | 'low-difficulty'; label: string; keywords: SeedKeyword[]; added: boolean; keywordCount: number; } interface SectorKeywordData { sectorSlug: string; sectorName: string; sectorId: number; options: SectorKeywordOption[]; } ``` #### 2. Data Loading Function Create `loadHighOpportunityKeywords()`: ```typescript const loadHighOpportunityKeywords = async () => { if (!activeSite || !activeSite.industry) { setSectorKeywordData([]); return; } setLoadingOpportunityKeywords(true); try { // 1. Get site sectors const siteSectors = await fetchSiteSectors(activeSite.id); // 2. Get industry data const industriesResponse = await fetchIndustries(); const industry = industriesResponse.industries?.find( i => i.id === activeSite.industry || i.slug === activeSite.industry_slug ); if (!industry?.id) { console.warn('Could not find industry information'); return; } // 3. Get already-attached keywords to mark as added const attachedSeedKeywordIds = new Set(); for (const sector of siteSectors) { try { const keywordsData = await fetchKeywords({ site_id: activeSite.id, sector_id: sector.id, page_size: 1000, }); (keywordsData.results || []).forEach((k: any) => { const seedKeywordId = k.seed_keyword_id || (k.seed_keyword && k.seed_keyword.id); if (seedKeywordId) { attachedSeedKeywordIds.add(Number(seedKeywordId)); } }); } catch (err) { console.warn(`Could not fetch attached keywords for sector ${sector.id}:`, err); } } // 4. Build sector keyword data const sectorData: SectorKeywordData[] = []; for (const siteSector of siteSectors) { if (!siteSector.is_active) continue; // Fetch all keywords for this sector const response = await fetchSeedKeywords({ industry: industry.id, sector: siteSector.industry_sector, page_size: 500, }); const sectorKeywords = response.results; // Top 50 by highest volume const highVolumeKeywords = [...sectorKeywords] .sort((a, b) => (b.volume || 0) - (a.volume || 0)) .slice(0, 50); // Top 50 by lowest difficulty const lowDifficultyKeywords = [...sectorKeywords] .sort((a, b) => (a.difficulty || 100) - (b.difficulty || 100)) .slice(0, 50); // Check if all keywords in each option are already added const hvAdded = highVolumeKeywords.every(kw => attachedSeedKeywordIds.has(Number(kw.id)) ); const ldAdded = lowDifficultyKeywords.every(kw => attachedSeedKeywordIds.has(Number(kw.id)) ); sectorData.push({ sectorSlug: siteSector.slug, sectorName: siteSector.name, sectorId: siteSector.id, options: [ { type: 'high-volume', label: 'Top 50 High Volume', keywords: highVolumeKeywords, added: hvAdded && highVolumeKeywords.length > 0, keywordCount: highVolumeKeywords.length, }, { type: 'low-difficulty', label: 'Top 50 Low Difficulty', keywords: lowDifficultyKeywords, added: ldAdded && lowDifficultyKeywords.length > 0, keywordCount: lowDifficultyKeywords.length, }, ], }); } setSectorKeywordData(sectorData); } catch (error: any) { console.error('Failed to load high opportunity keywords:', error); toast.error(`Failed to load high opportunity keywords: ${error.message}`); } finally { setLoadingOpportunityKeywords(false); } }; ``` #### 3. Add Keywords Function Create `handleAddSectorKeywords()`: ```typescript const handleAddSectorKeywords = async ( sectorSlug: string, optionType: 'high-volume' | 'low-difficulty' ) => { const sector = sectorKeywordData.find(s => s.sectorSlug === sectorSlug); if (!sector || !activeSite) return; const option = sector.options.find(o => o.type === optionType); if (!option || option.added || option.keywords.length === 0) return; const addingKey = `${sectorSlug}-${optionType}`; setAddingOption(addingKey); try { const seedKeywordIds = option.keywords.map(kw => kw.id); const result = await addSeedKeywordsToWorkflow( seedKeywordIds, activeSite.id, sector.sectorId ); if (result.success && result.created > 0) { // Mark option as added setSectorKeywordData(prev => prev.map(s => s.sectorSlug === sectorSlug ? { ...s, options: s.options.map(o => o.type === optionType ? { ...o, added: true } : o ), } : s ) ); let message = `Added ${result.created} keywords to ${sector.sectorName}`; if (result.skipped && result.skipped > 0) { message += ` (${result.skipped} already exist)`; } toast.success(message); // Reload the main table to reflect changes loadSeedKeywords(); } else if (result.errors && result.errors.length > 0) { toast.error(result.errors[0]); } else { toast.warning('No keywords were added. They may already exist in your workflow.'); } } catch (err: any) { toast.error(err.message || 'Failed to add keywords to workflow'); } finally { setAddingOption(null); } }; ``` #### 4. UI Component Create `HighOpportunityKeywordsSection` component within the file: ```typescript const HighOpportunityKeywordsSection = () => { if (!showHighOpportunity) return null; if (!activeSite || sectorKeywordData.length === 0) return null; const addedCount = sectorKeywordData.reduce( (acc, s) => acc + s.options .filter(o => o.added) .reduce((sum, o) => sum + o.keywordCount, 0), 0 ); return (
{/* Header */}

High Opportunity Keywords

Add top keywords for each of your sectors. Keywords will be added to your planner workflow.

{/* Loading State */} {loadingOpportunityKeywords ? (
) : ( <> {/* Sector Grid */}
{sectorKeywordData.map((sector) => (
{/* Sector Name */}

{sector.sectorName}

{/* Options Cards */} {sector.options.map((option) => { const addingKey = `${sector.sectorSlug}-${option.type}`; const isAdding = addingOption === addingKey; return ( {/* Option Header */}
{option.label}

{option.keywordCount} keywords

{option.added ? ( Added ) : ( )}
{/* Sample Keywords */}
{option.keywords.slice(0, 3).map((kw) => ( {kw.keyword} ))} {option.keywordCount > 3 && ( +{option.keywordCount - 3} more )}
); })}
))}
{/* Success Summary */} {addedCount > 0 && (
{addedCount} keywords added to your workflow
)} )}
); }; ``` #### 5. Integration Points **In `IndustriesSectorsKeywords.tsx`:** 1. **Add imports:** ```typescript import { CheckCircleIcon } from '../../icons'; import { fetchIndustries, fetchKeywords } from '../../services/api'; ``` 2. **Add state variables** (see #1 above) 3. **Add useEffect to load opportunity keywords:** ```typescript useEffect(() => { if (activeSite && activeSite.id && showHighOpportunity) { loadHighOpportunityKeywords(); } }, [activeSite?.id, showHighOpportunity]); ``` 4. **Insert component before TablePageTemplate:** ```tsx return ( <> {/* High Opportunity Keywords Section */} {/* Existing sector banner */} {!activeSector && activeSite && ( ... )} {/* Import Modal */} ); ``` ### Visual Design Details #### Colors & Styling - **Card default:** White bg, gray-200 border, hover: brand-300 border - **Card added:** success-50 bg, success-300 border, success badge - **Button:** Primary variant, size xs for cards - **Badges:** xs size for keywords, soft variant - **Grid:** 1 column on mobile, 2 on md, 3 on lg #### Responsive Behavior - **Mobile (< 768px):** Single column, sectors stack vertically - **Tablet (768px - 1024px):** 2 columns - **Desktop (> 1024px):** 3 columns #### User Interactions 1. **"Add All" button:** - Disabled during adding (shows "Adding...") - Disabled if no sector selected (with tooltip) - Disappears after successful add (replaced with "Added" badge) 2. **Success feedback:** - Toast notification with count - Green success card appears at bottom when any keywords added - Card styling changes to success state - Badge changes to success badge with checkmark 3. **Hide/Show:** - "Hide" button in header to collapse section - Could add "Show High Opportunity Keywords" link when hidden ### Error Handling 1. **No active site:** - Don't render the section - Show existing WorkflowGuide 2. **No sectors:** - Don't render the section - Show existing sector selection banner 3. **API failures:** - Show toast error - Log to console - Don't crash the page 4. **No keywords found:** - Show empty state or hide section - Log warning to console ### Performance Considerations 1. **Lazy loading:** - Only load opportunity keywords when section is visible - Use `showHighOpportunity` state flag 2. **Caching:** - Store loaded keyword data in state - Only reload on site/sector change 3. **Batch API calls:** - Load all sector keywords in parallel if possible - Use Promise.all() for concurrent requests 4. **Pagination:** - Fetch 500 keywords max per sector - This should cover top 50 for both options with margin ### Testing Scenarios 1. **Happy path:** - User has active site with 3 sectors - Each sector has 50+ keywords - Click "Add All" on each option - Verify keywords added to workflow - Verify success feedback shown 2. **Edge cases:** - No active site: Section hidden - No sectors: Section hidden - No keywords for sector: Empty cards - Already added keywords: Show "Added" badge - API failure: Show error toast 3. **Interaction:** - Add from high opportunity section - Verify table below refreshes - Verify count updates in header - Verify keywords marked as "Added" in table 4. **Responsive:** - Test on mobile, tablet, desktop - Verify grid layout adapts - Verify cards are readable ## Implementation Steps ### Phase 1: Core Functionality (Day 1) 1. ✅ Create plan document (this file) 2. Add state management and types 3. Implement `loadHighOpportunityKeywords()` function 4. Implement `handleAddSectorKeywords()` function 5. Test data loading and API integration ### Phase 2: UI Components (Day 1-2) 6. Create `HighOpportunityKeywordsSection` component 7. Build sector cards with options 8. Add loading states 9. Add success states and feedback 10. Style according to design system ### Phase 3: Integration (Day 2) 11. Integrate with existing page 12. Connect to existing state (activeSite, activeSector) 13. Ensure table refreshes after keywords added 14. Test hide/show functionality ### Phase 4: Polish & Testing (Day 2-3) 15. Add responsive styles 16. Test on different screen sizes 17. Handle edge cases and errors 18. Add tooltips and help text 19. Performance optimization 20. Code review and cleanup ## Dependencies - ✅ Existing API functions in `services/api.ts` - ✅ `fetchSeedKeywords()` - Fetch seed keywords - ✅ `fetchSiteSectors()` - Get sectors for site - ✅ `fetchIndustries()` - Get industry data - ✅ `fetchKeywords()` - Check already-attached keywords - ✅ `addSeedKeywordsToWorkflow()` - Bulk add keywords - ✅ Existing components - ✅ `Card` component - ✅ `Badge` component - ✅ `Button` component - ✅ `CheckCircleIcon` icon ## Success Criteria 1. **Functional:** - [ ] High opportunity keywords section displays at top of page - [ ] Shows sectors with 2 options each (High Volume, Low Difficulty) - [ ] "Add All" button adds keywords to workflow successfully - [ ] Success feedback shown after adding - [ ] Main table refreshes to show added keywords - [ ] Hide/show functionality works 2. **Visual:** - [ ] Matches wizard step 4 design - [ ] Responsive on all screen sizes - [ ] Success states clearly visible - [ ] Loading states smooth 3. **UX:** - [ ] Fast loading (< 2 seconds) - [ ] Clear feedback on actions - [ ] Tooltips help users understand - [ ] Graceful error handling - [ ] No blocking errors ## Future Enhancements 1. **Expand/collapse sectors:** Allow users to collapse individual sectors 2. **Keyword preview modal:** Click on "+X more" to see full list 3. **Custom keyword counts:** Let users choose how many keywords to add (25, 50, 100) 4. **Filter by metrics:** Show only keywords above certain volume/below certain difficulty 5. **Save preferences:** Remember hidden/shown state 6. **Analytics:** Track which options users add most frequently 7. **Smart recommendations:** Suggest sectors based on site content 8. **Batch operations:** "Add all high volume" button to add from all sectors at once ## Notes - This feature improves onboarding by bringing wizard functionality into the main app - Users can quickly populate their workflow without searching/filtering - Reduces friction for new users who don't know which keywords to target - Leverages existing curated seed keyword database - No new API endpoints required - uses existing bulk add functionality ## Related Files **Frontend:** - `/frontend/src/pages/Setup/IndustriesSectorsKeywords.tsx` - Target file for implementation - `/frontend/src/components/onboarding/steps/Step4AddKeywords.tsx` - Reference implementation - `/frontend/src/services/api.ts` - API functions **Backend:** - `/backend/igny8_core/api/views/keywords.py` - Keywords API - `/backend/igny8_core/api/views/seed_keywords.py` - Seed keywords API - `/backend/igny8_core/business/seed_keywords.py` - Bulk add logic **Docs:** - `/docs/40-WORKFLOWS/ADD_KEYWORDS.md` - Add keywords workflow documentation - `/docs/10-MODULES/PLANNER.md` - Planner module documentation