diff --git a/backend/igny8_core/modules/planner/views.py b/backend/igny8_core/modules/planner/views.py
index b4eee2e6..6a1b5394 100644
--- a/backend/igny8_core/modules/planner/views.py
+++ b/backend/igny8_core/modules/planner/views.py
@@ -321,12 +321,29 @@ class KeywordViewSet(SiteSectorModelViewSet):
try:
# Validate industry/sector match
if site.industry != seed_keyword.industry:
- errors.append(f"SeedKeyword '{seed_keyword.keyword}' industry mismatch")
+ errors.append(
+ f"Keyword '{seed_keyword.keyword}': industry mismatch "
+ f"(site={site.industry.name if site.industry else 'None'}, "
+ f"seed={seed_keyword.industry.name if seed_keyword.industry else 'None'})"
+ )
+ skipped_count += 1
+ continue
+
+ # Check if sector has industry_sector set
+ if not sector.industry_sector:
+ errors.append(
+ f"Keyword '{seed_keyword.keyword}': sector '{sector.name}' has no industry_sector set. "
+ f"Please update the sector to reference an industry sector."
+ )
skipped_count += 1
continue
if sector.industry_sector != seed_keyword.sector:
- errors.append(f"SeedKeyword '{seed_keyword.keyword}' sector mismatch")
+ errors.append(
+ f"Keyword '{seed_keyword.keyword}': sector mismatch "
+ f"(sector={sector.industry_sector.name if sector.industry_sector else 'None'}, "
+ f"seed={seed_keyword.sector.name if seed_keyword.sector else 'None'})"
+ )
skipped_count += 1
continue
diff --git a/frontend/src/pages/Planner/KeywordOpportunities.tsx b/frontend/src/pages/Planner/KeywordOpportunities.tsx
index 34b3ce03..e9593d94 100644
--- a/frontend/src/pages/Planner/KeywordOpportunities.tsx
+++ b/frontend/src/pages/Planner/KeywordOpportunities.tsx
@@ -297,25 +297,43 @@ export default function KeywordOpportunities() {
);
if (result.success) {
- toast.success(`Successfully added ${result.created} keyword(s) to workflow`);
+ // Show success message with created count
+ if (result.created > 0) {
+ toast.success(`Successfully added ${result.created} keyword(s) to workflow`);
+ }
- // Track these as recently added to preserve state during reload
- seedKeywordIds.forEach(id => {
- recentlyAddedRef.current.add(id);
- });
+ // Show skipped count if any
+ if (result.skipped > 0) {
+ toast.warning(`${result.skipped} keyword(s) were skipped (already exist or validation failed)`);
+ }
+
+ // Show detailed errors if any
+ if (result.errors && result.errors.length > 0) {
+ result.errors.forEach((error: string) => {
+ toast.error(error, { duration: 8000 });
+ });
+ }
+
+ // Only track and mark as added if actually created
+ if (result.created > 0) {
+ // Track these as recently added to preserve state during reload
+ seedKeywordIds.forEach(id => {
+ recentlyAddedRef.current.add(id);
+ });
+
+ // Immediately update state to mark keywords as added - this gives instant feedback
+ setSeedKeywords(prevKeywords =>
+ prevKeywords.map(kw =>
+ seedKeywordIds.includes(kw.id)
+ ? { ...kw, isAdded: true }
+ : kw
+ )
+ );
+ }
// Clear selection
setSelectedIds([]);
- // Immediately update state to mark keywords as added - this gives instant feedback
- setSeedKeywords(prevKeywords =>
- prevKeywords.map(kw =>
- seedKeywordIds.includes(kw.id)
- ? { ...kw, isAdded: true }
- : kw
- )
- );
-
// Don't reload immediately - the state is already updated
// The recentlyAddedRef will ensure they stay marked as added
// Only reload if user changes filters/pagination
@@ -546,6 +564,13 @@ export default function KeywordOpportunities() {
],
},
],
+ bulkActions: !activeSector ? [] : [
+ {
+ key: 'add_selected_to_workflow',
+ label: 'Add Selected to Workflow',
+ variant: 'primary' as const,
+ },
+ ],
};
}, [activeSector]);
@@ -555,6 +580,30 @@ export default function KeywordOpportunities() {
title="Keyword Opportunities"
badge={{ icon:
+ Please select a sector from the dropdown above to enable adding keywords to your workflow. Keywords must be added to a specific sector. +
++ Please select a sector from the dropdown above to enable adding keywords to your workflow. Keywords must be added to a specific sector. +
+