diff --git a/backend/igny8_core/ai/functions/auto_cluster.py b/backend/igny8_core/ai/functions/auto_cluster.py index 2f94b39a..e1b61f4c 100644 --- a/backend/igny8_core/ai/functions/auto_cluster.py +++ b/backend/igny8_core/ai/functions/auto_cluster.py @@ -97,7 +97,6 @@ class AutoClusterFunction(BaseAIFunction): 'keyword': kw.keyword, 'volume': kw.volume, 'difficulty': kw.difficulty, - 'intent': kw.intent, } for kw in keywords ], @@ -111,7 +110,7 @@ class AutoClusterFunction(BaseAIFunction): # Format keywords keywords_text = '\n'.join([ - f"- {kw['keyword']} (Volume: {kw['volume']}, Difficulty: {kw['difficulty']}, Intent: {kw['intent']})" + f"- {kw['keyword']} (Volume: {kw['volume']}, Difficulty: {kw['difficulty']})" for kw in keyword_data ]) diff --git a/backend/igny8_core/api/tests/test_integration_base.py b/backend/igny8_core/api/tests/test_integration_base.py index 58c7fd52..a86df97c 100644 --- a/backend/igny8_core/api/tests/test_integration_base.py +++ b/backend/igny8_core/api/tests/test_integration_base.py @@ -79,7 +79,7 @@ class IntegrationTestBase(TestCase): sector=self.industry_sector, volume=1000, difficulty=50, - intent="informational" + country="US" ) # Authenticate client diff --git a/backend/igny8_core/auth/admin.py b/backend/igny8_core/auth/admin.py index 8b054ac0..633e7f21 100644 --- a/backend/igny8_core/auth/admin.py +++ b/backend/igny8_core/auth/admin.py @@ -548,18 +548,18 @@ class IndustrySectorAdmin(Igny8ModelAdmin): @admin.register(SeedKeyword) class SeedKeywordAdmin(Igny8ModelAdmin): """SeedKeyword admin - Global reference data, no account filtering""" - list_display = ['keyword', 'industry', 'sector', 'volume', 'difficulty', 'intent', 'is_active', 'created_at'] - list_filter = ['is_active', 'industry', 'sector', 'intent'] + list_display = ['keyword', 'industry', 'sector', 'volume', 'difficulty', 'country', 'is_active', 'created_at'] + list_filter = ['is_active', 'industry', 'sector', 'country'] search_fields = ['keyword'] readonly_fields = ['created_at', 'updated_at'] actions = ['delete_selected'] # Enable bulk delete fieldsets = ( ('Keyword Info', { - 'fields': ('keyword', 'industry', 'sector', 'is_active') + 'fields': ('keyword', 'industry', 'sector', 'country', 'is_active') }), ('SEO Metrics', { - 'fields': ('volume', 'difficulty', 'intent') + 'fields': ('volume', 'difficulty') }), ('Timestamps', { 'fields': ('created_at', 'updated_at') diff --git a/backend/igny8_core/auth/migrations/0018_add_country_remove_intent_seedkeyword.py b/backend/igny8_core/auth/migrations/0018_add_country_remove_intent_seedkeyword.py new file mode 100644 index 00000000..ced24ba6 --- /dev/null +++ b/backend/igny8_core/auth/migrations/0018_add_country_remove_intent_seedkeyword.py @@ -0,0 +1,30 @@ +# Generated by Django 5.2.9 on 2025-12-17 06:04 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('igny8_core_auth', '0017_add_history_tracking'), + ] + + operations = [ + migrations.RemoveIndex( + model_name='seedkeyword', + name='igny8_seed__intent_15020d_idx', + ), + migrations.RemoveField( + model_name='seedkeyword', + name='intent', + ), + migrations.AddField( + model_name='seedkeyword', + name='country', + field=models.CharField(choices=[('US', 'United States'), ('CA', 'Canada'), ('GB', 'United Kingdom'), ('AE', 'United Arab Emirates'), ('AU', 'Australia'), ('IN', 'India'), ('PK', 'Pakistan')], default='US', help_text='Target country for this keyword', max_length=2), + ), + migrations.AddIndex( + model_name='seedkeyword', + index=models.Index(fields=['country'], name='igny8_seed__country_4127a5_idx'), + ), + ] diff --git a/backend/igny8_core/auth/models.py b/backend/igny8_core/auth/models.py index f722c374..00ec7b30 100644 --- a/backend/igny8_core/auth/models.py +++ b/backend/igny8_core/auth/models.py @@ -517,11 +517,14 @@ class SeedKeyword(models.Model): These are canonical keywords that can be imported into account-specific Keywords. Non-deletable global reference data. """ - INTENT_CHOICES = [ - ('informational', 'Informational'), - ('navigational', 'Navigational'), - ('commercial', 'Commercial'), - ('transactional', 'Transactional'), + COUNTRY_CHOICES = [ + ('US', 'United States'), + ('CA', 'Canada'), + ('GB', 'United Kingdom'), + ('AE', 'United Arab Emirates'), + ('AU', 'Australia'), + ('IN', 'India'), + ('PK', 'Pakistan'), ] keyword = models.CharField(max_length=255, db_index=True) @@ -533,7 +536,7 @@ class SeedKeyword(models.Model): validators=[MinValueValidator(0), MaxValueValidator(100)], help_text='Keyword difficulty (0-100)' ) - intent = models.CharField(max_length=50, choices=INTENT_CHOICES, default='informational') + country = models.CharField(max_length=2, choices=COUNTRY_CHOICES, default='US', help_text='Target country for this keyword') is_active = models.BooleanField(default=True, db_index=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) @@ -547,7 +550,7 @@ class SeedKeyword(models.Model): models.Index(fields=['keyword']), models.Index(fields=['industry', 'sector']), models.Index(fields=['industry', 'sector', 'is_active']), - models.Index(fields=['intent']), + models.Index(fields=['country']), ] ordering = ['keyword'] diff --git a/backend/igny8_core/auth/serializers.py b/backend/igny8_core/auth/serializers.py index 01965177..1978ff83 100644 --- a/backend/igny8_core/auth/serializers.py +++ b/backend/igny8_core/auth/serializers.py @@ -524,14 +524,14 @@ class SeedKeywordSerializer(serializers.ModelSerializer): industry_slug = serializers.CharField(source='industry.slug', read_only=True) sector_name = serializers.CharField(source='sector.name', read_only=True) sector_slug = serializers.CharField(source='sector.slug', read_only=True) - intent_display = serializers.CharField(source='get_intent_display', read_only=True) + country_display = serializers.CharField(source='get_country_display', read_only=True) class Meta: model = SeedKeyword fields = [ 'id', 'keyword', 'industry', 'industry_name', 'industry_slug', 'sector', 'sector_name', 'sector_slug', - 'volume', 'difficulty', 'intent', 'intent_display', + 'volume', 'difficulty', 'country', 'country_display', 'is_active', 'created_at', 'updated_at' ] read_only_fields = ['created_at', 'updated_at'] diff --git a/backend/igny8_core/auth/views.py b/backend/igny8_core/auth/views.py index ae66e0ea..9b42990f 100644 --- a/backend/igny8_core/auth/views.py +++ b/backend/igny8_core/auth/views.py @@ -839,7 +839,7 @@ class SeedKeywordViewSet(viewsets.ReadOnlyModelViewSet): search_fields = ['keyword'] ordering_fields = ['keyword', 'volume', 'difficulty', 'created_at'] ordering = ['keyword'] - filterset_fields = ['industry', 'sector', 'intent', 'is_active'] + filterset_fields = ['industry', 'sector', 'country', 'is_active'] def retrieve(self, request, *args, **kwargs): """Override retrieve to return unified format""" @@ -877,7 +877,7 @@ class SeedKeywordViewSet(viewsets.ReadOnlyModelViewSet): def import_seed_keywords(self, request): """ Import seed keywords from CSV (Admin/Superuser only). - Expected columns: keyword, industry_name, sector_name, volume, difficulty, intent + Expected columns: keyword, industry_name, sector_name, volume, difficulty, country """ import csv from django.db import transaction @@ -960,7 +960,7 @@ class SeedKeywordViewSet(viewsets.ReadOnlyModelViewSet): sector=sector, volume=int(row.get('volume', 0) or 0), difficulty=int(row.get('difficulty', 0) or 0), - intent=row.get('intent', 'informational') or 'informational', + country=row.get('country', 'US') or 'US', is_active=True ) imported_count += 1 @@ -1487,9 +1487,9 @@ def seedkeyword_csv_template(request): response['Content-Disposition'] = 'attachment; filename="seedkeyword_template.csv"' writer = csv.writer(response) - writer.writerow(['keyword', 'industry', 'sector', 'volume', 'difficulty', 'intent', 'is_active']) - writer.writerow(['python programming', 'Technology', 'Software Development', '10000', '45', 'Informational', 'true']) - writer.writerow(['medical software', 'Healthcare', 'Healthcare IT', '5000', '60', 'Commercial', 'true']) + writer.writerow(['keyword', 'industry', 'sector', 'volume', 'difficulty', 'country', 'is_active']) + writer.writerow(['python programming', 'Technology', 'Software Development', '10000', '45', 'US', 'true']) + writer.writerow(['medical software', 'Healthcare', 'Healthcare IT', '5000', '60', 'CA', 'true']) return response @@ -1534,7 +1534,7 @@ def seedkeyword_csv_import(request): defaults={ 'volume': int(row.get('volume', 0)), 'difficulty': int(row.get('difficulty', 0)), - 'intent': row.get('intent', 'Informational'), + 'country': row.get('country', 'US'), 'is_active': is_active } ) diff --git a/backend/igny8_core/business/automation/models.py b/backend/igny8_core/business/automation/models.py index 597686ba..8f4836c5 100644 --- a/backend/igny8_core/business/automation/models.py +++ b/backend/igny8_core/business/automation/models.py @@ -24,7 +24,7 @@ class AutomationConfig(models.Model): scheduled_time = models.TimeField(default='02:00', help_text="Time to run (e.g., 02:00)") # Batch sizes per stage - stage_1_batch_size = models.IntegerField(default=20, help_text="Keywords per batch") + stage_1_batch_size = models.IntegerField(default=50, help_text="Keywords per batch") stage_2_batch_size = models.IntegerField(default=1, help_text="Clusters at a time") stage_3_batch_size = models.IntegerField(default=20, help_text="Ideas per batch") stage_4_batch_size = models.IntegerField(default=1, help_text="Tasks - sequential") diff --git a/backend/igny8_core/business/automation/services/automation_service.py b/backend/igny8_core/business/automation/services/automation_service.py index e40d65bc..0b73177e 100644 --- a/backend/igny8_core/business/automation/services/automation_service.py +++ b/backend/igny8_core/business/automation/services/automation_service.py @@ -637,6 +637,7 @@ class AutomationService: content_type=idea.content_type or 'post', content_structure=idea.content_structure or 'article', keywords=keywords_str, + word_count=idea.estimated_word_count, status='queued', account=idea.account, site=idea.site, diff --git a/backend/igny8_core/business/automation/views.py b/backend/igny8_core/business/automation/views.py index 18197113..6eb67c75 100644 --- a/backend/igny8_core/business/automation/views.py +++ b/backend/igny8_core/business/automation/views.py @@ -82,7 +82,7 @@ class AutomationViewSet(viewsets.ViewSet): "is_enabled": true, "frequency": "daily", "scheduled_time": "02:00", - "stage_1_batch_size": 20, + "stage_1_batch_size": 50, ... } """ diff --git a/backend/igny8_core/business/planning/models.py b/backend/igny8_core/business/planning/models.py index 65651e11..42adf528 100644 --- a/backend/igny8_core/business/planning/models.py +++ b/backend/igny8_core/business/planning/models.py @@ -131,9 +131,9 @@ class Keywords(SoftDeletableModel, SiteSectorBaseModel): return self.difficulty_override if self.difficulty_override is not None else (self.seed_keyword.difficulty if self.seed_keyword else 0) @property - def intent(self): - """Get intent from seed_keyword""" - return self.seed_keyword.intent if self.seed_keyword else 'informational' + def country(self): + """Get country from seed_keyword""" + return self.seed_keyword.country if self.seed_keyword else 'US' def save(self, *args, **kwargs): """Validate that seed_keyword's industry/sector matches site's industry/sector""" diff --git a/backend/igny8_core/business/planning/services/clustering_service.py b/backend/igny8_core/business/planning/services/clustering_service.py index 49838c3e..bf6f77c3 100644 --- a/backend/igny8_core/business/planning/services/clustering_service.py +++ b/backend/igny8_core/business/planning/services/clustering_service.py @@ -38,10 +38,10 @@ class ClusteringService: 'error': 'No keyword IDs provided' } - if len(keyword_ids) > 20: + if len(keyword_ids) > 50: return { 'success': False, - 'error': 'Maximum 20 keywords allowed for clustering' + 'error': 'Maximum 50 keywords allowed for clustering' } # Check credits (fixed cost per clustering operation) diff --git a/backend/igny8_core/modules/planner/admin.py b/backend/igny8_core/modules/planner/admin.py index 2ae78276..b887126e 100644 --- a/backend/igny8_core/modules/planner/admin.py +++ b/backend/igny8_core/modules/planner/admin.py @@ -18,7 +18,7 @@ class KeywordsResource(resources.ModelResource): class Meta: model = Keywords fields = ('id', 'keyword', 'seed_keyword__keyword', 'site__name', 'sector__name', - 'cluster__name', 'volume', 'difficulty', 'intent', 'status', 'created_at') + 'cluster__name', 'volume', 'difficulty', 'country', 'status', 'created_at') export_order = fields @@ -55,11 +55,11 @@ class ClustersAdmin(SiteSectorAdminMixin, Igny8ModelAdmin): @admin.register(Keywords) class KeywordsAdmin(ExportMixin, SiteSectorAdminMixin, Igny8ModelAdmin): resource_class = KeywordsResource - list_display = ['keyword', 'seed_keyword', 'site', 'sector', 'cluster', 'volume', 'difficulty', 'intent', 'status', 'created_at'] + list_display = ['keyword', 'seed_keyword', 'site', 'sector', 'cluster', 'volume', 'difficulty', 'country', 'status', 'created_at'] list_editable = ['status'] # Enable inline editing for status list_filter = [ ('status', ChoicesDropdownFilter), - ('intent', ChoicesDropdownFilter), + ('country', ChoicesDropdownFilter), ('site', RelatedDropdownFilter), ('sector', RelatedDropdownFilter), ('cluster', RelatedDropdownFilter), diff --git a/backend/igny8_core/modules/planner/management/commands/add_keywords_to_sectors.py b/backend/igny8_core/modules/planner/management/commands/add_keywords_to_sectors.py index 5b6ffad2..7ec416c8 100644 --- a/backend/igny8_core/modules/planner/management/commands/add_keywords_to_sectors.py +++ b/backend/igny8_core/modules/planner/management/commands/add_keywords_to_sectors.py @@ -109,7 +109,6 @@ class Command(BaseCommand): 'account': site.account, 'volume': 1000 + (created_count * 100), # Varying volumes 'difficulty': 30 + (created_count * 10), # Varying difficulty (0-100 scale) - 'intent': 'informational' if created_count % 2 == 0 else 'commercial', 'status': 'active', } ) diff --git a/backend/igny8_core/modules/planner/management/commands/create_home_garden_industry.py b/backend/igny8_core/modules/planner/management/commands/create_home_garden_industry.py index 8ed01c30..c19c1516 100644 --- a/backend/igny8_core/modules/planner/management/commands/create_home_garden_industry.py +++ b/backend/igny8_core/modules/planner/management/commands/create_home_garden_industry.py @@ -243,7 +243,6 @@ class Command(BaseCommand): 'account': site.account, 'volume': 500 + (created_count * 50), # Varying volumes 'difficulty': 20 + (created_count * 8), # Varying difficulty (0-100 scale) - 'intent': 'informational' if created_count % 2 == 0 else 'commercial', 'status': 'active', } ) diff --git a/backend/igny8_core/modules/planner/management/commands/migrate_keywords_to_seed.py b/backend/igny8_core/modules/planner/management/commands/migrate_keywords_to_seed.py index 3e3ce9ea..648d2284 100755 --- a/backend/igny8_core/modules/planner/management/commands/migrate_keywords_to_seed.py +++ b/backend/igny8_core/modules/planner/management/commands/migrate_keywords_to_seed.py @@ -93,7 +93,7 @@ class Command(BaseCommand): sector=industry_sector, volume=keyword.volume or 0, difficulty=keyword.difficulty or 0, - intent=keyword.intent or 'informational', + country='US', # Default country for migration is_active=True ) created_count += 1 diff --git a/backend/igny8_core/modules/planner/serializers.py b/backend/igny8_core/modules/planner/serializers.py index 73635a98..e62b42b3 100644 --- a/backend/igny8_core/modules/planner/serializers.py +++ b/backend/igny8_core/modules/planner/serializers.py @@ -13,7 +13,7 @@ class KeywordSerializer(serializers.ModelSerializer): keyword = serializers.CharField(read_only=True) # From seed_keyword.keyword volume = serializers.IntegerField(read_only=True) # From seed_keyword.volume or volume_override difficulty = serializers.IntegerField(read_only=True) # From seed_keyword.difficulty or difficulty_override - intent = serializers.CharField(read_only=True) # From seed_keyword.intent + country = serializers.CharField(read_only=True) # From seed_keyword.country # SeedKeyword relationship # Optional for create - can either provide seed_keyword_id OR custom keyword fields @@ -24,11 +24,11 @@ class KeywordSerializer(serializers.ModelSerializer): custom_keyword = serializers.CharField(write_only=True, required=False, allow_blank=False) custom_volume = serializers.IntegerField(write_only=True, required=False, allow_null=True) custom_difficulty = serializers.IntegerField(write_only=True, required=False, allow_null=True) - custom_intent = serializers.ChoiceField( + custom_country = serializers.ChoiceField( write_only=True, required=False, - choices=['informational', 'navigational', 'transactional', 'commercial'], - default='informational' + choices=['US', 'CA', 'GB', 'AE', 'AU', 'IN', 'PK'], + default='US' ) # Overrides @@ -50,11 +50,11 @@ class KeywordSerializer(serializers.ModelSerializer): 'keyword', 'volume', 'difficulty', - 'intent', + 'country', 'custom_keyword', # Write-only field for creating custom keywords 'custom_volume', # Write-only 'custom_difficulty', # Write-only - 'custom_intent', # Write-only + 'custom_country', # Write-only 'volume_override', 'difficulty_override', 'cluster_id', @@ -67,7 +67,7 @@ class KeywordSerializer(serializers.ModelSerializer): 'sector_id', 'account_id', ] - read_only_fields = ['id', 'created_at', 'updated_at', 'account_id', 'keyword', 'volume', 'difficulty', 'intent'] + read_only_fields = ['id', 'created_at', 'updated_at', 'account_id', 'keyword', 'volume', 'difficulty', 'country'] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -106,7 +106,7 @@ class KeywordSerializer(serializers.ModelSerializer): custom_keyword = validated_data.pop('custom_keyword', None) custom_volume = validated_data.pop('custom_volume', None) custom_difficulty = validated_data.pop('custom_difficulty', None) - custom_intent = validated_data.pop('custom_intent', None) or 'informational' + custom_country = validated_data.pop('custom_country', None) or 'US' # Get site and sector - they're passed as objects via save() in the view site = validated_data.get('site') @@ -132,7 +132,7 @@ class KeywordSerializer(serializers.ModelSerializer): defaults={ 'volume': custom_volume or 0, 'difficulty': custom_difficulty or 0, - 'intent': custom_intent or 'informational', + 'country': custom_country or 'US', 'is_active': True, } ) @@ -162,7 +162,7 @@ class KeywordSerializer(serializers.ModelSerializer): validated_data.pop('custom_keyword', None) validated_data.pop('custom_volume', None) validated_data.pop('custom_difficulty', None) - validated_data.pop('custom_intent', None) + validated_data.pop('custom_country', None) # seed_keyword_id is optional for updates - only update if provided if 'seed_keyword_id' in validated_data: diff --git a/backend/igny8_core/modules/planner/views.py b/backend/igny8_core/modules/planner/views.py index 9b84b763..f7bcb907 100644 --- a/backend/igny8_core/modules/planner/views.py +++ b/backend/igny8_core/modules/planner/views.py @@ -55,7 +55,7 @@ class KeywordViewSet(SiteSectorModelViewSet): ordering = ['-created_at'] # Default ordering (newest first) # Filter configuration - filter by status, cluster_id, and seed_keyword fields - filterset_fields = ['status', 'cluster_id', 'seed_keyword__intent', 'seed_keyword_id'] + filterset_fields = ['status', 'cluster_id', 'seed_keyword__country', 'seed_keyword_id'] def get_queryset(self): """ @@ -475,7 +475,7 @@ class KeywordViewSet(SiteSectorModelViewSet): writer = csv.writer(response) # Header row - writer.writerow(['ID', 'Keyword', 'Volume', 'Difficulty', 'Intent', 'Status', 'Cluster ID', 'Created At']) + writer.writerow(['ID', 'Keyword', 'Volume', 'Difficulty', 'Country', 'Status', 'Cluster ID', 'Created At']) # Data rows for keyword in keywords: @@ -484,7 +484,7 @@ class KeywordViewSet(SiteSectorModelViewSet): keyword.keyword, keyword.volume, keyword.difficulty, - keyword.intent, + keyword.country, keyword.status, keyword.cluster_id or '', keyword.created_at.isoformat() if keyword.created_at else '', @@ -631,12 +631,13 @@ class KeywordViewSet(SiteSectorModelViewSet): skipped_count += 1 continue - # Create keyword + # Note: This direct creation bypasses seed_keyword linkage + # Keywords should ideally be created through seed_keyword FK + # Country comes from seed_keyword.country property Keywords.objects.create( keyword=keyword_text, volume=int(row.get('volume', 0) or 0), difficulty=int(row.get('difficulty', 0) or 0), - intent=row.get('intent', 'informational') or 'informational', status=row.get('status', 'new') or 'new', site=site, sector=sector, @@ -1193,6 +1194,7 @@ class ContentIdeasViewSet(SiteSectorModelViewSet): content_structure=idea.content_structure or 'article', taxonomy_term=None, # Can be set later if taxonomy is available keywords=keywords_str, # Comma-separated keywords string + word_count=idea.estimated_word_count, # Copy word count from idea status='queued', account=idea.account, site=idea.site, diff --git a/backend/igny8_core/utils/ai_processor.py b/backend/igny8_core/utils/ai_processor.py index ca34b73e..799e6334 100644 --- a/backend/igny8_core/utils/ai_processor.py +++ b/backend/igny8_core/utils/ai_processor.py @@ -1050,7 +1050,7 @@ Make sure each prompt is detailed enough for image generation, describing the vi def cluster_keywords( self, - keywords: List[Dict[str, Any]], # List of keyword dicts with 'keyword', 'volume', 'difficulty', 'intent' + keywords: List[Dict[str, Any]], # List of keyword dicts with 'keyword', 'volume', 'difficulty' sector_name: Optional[str] = None, account=None, response_steps=None, @@ -1069,7 +1069,7 @@ Make sure each prompt is detailed enough for image generation, describing the vi Based on reference plugin's clustering prompt. Args: - keywords: List of keyword dicts with keyword, volume, difficulty, intent + keywords: List of keyword dicts with keyword, volume, difficulty sector_name: Optional sector name for context account: Optional account for getting custom prompts @@ -1085,7 +1085,7 @@ Make sure each prompt is detailed enough for image generation, describing the vi # Format keywords for prompt keywords_text = '\n'.join([ - f"- {kw.get('keyword', '')} (Volume: {kw.get('volume', 0)}, Difficulty: {kw.get('difficulty', 0)}, Intent: {kw.get('intent', 'unknown')})" + f"- {kw.get('keyword', '')} (Volume: {kw.get('volume', 0)}, Difficulty: {kw.get('difficulty', 0)})" for kw in keywords ]) diff --git a/frontend/src/config/forms/keyword-form.config.ts b/frontend/src/config/forms/keyword-form.config.ts index aa35b3c8..2f29ff23 100644 --- a/frontend/src/config/forms/keyword-form.config.ts +++ b/frontend/src/config/forms/keyword-form.config.ts @@ -55,16 +55,19 @@ export const getKeywordFormConfig = (options?: { required: false, }, { - key: 'intent', - label: 'Intent', + key: 'country', + label: 'Country', type: 'select', - value: 'informational', + value: 'US', onChange: () => {}, options: [ - { value: 'informational', label: 'Informational' }, - { value: 'transactional', label: 'Transactional' }, - { value: 'navigational', label: 'Navigational' }, - { value: 'commercial', label: 'Commercial' }, + { value: 'US', label: 'United States' }, + { value: 'CA', label: 'Canada' }, + { value: 'GB', label: 'United Kingdom' }, + { value: 'AE', label: 'United Arab Emirates' }, + { value: 'AU', label: 'Australia' }, + { value: 'IN', label: 'India' }, + { value: 'PK', label: 'Pakistan' }, ], required: false, }, diff --git a/frontend/src/config/import-export.config.tsx b/frontend/src/config/import-export.config.tsx index 398a9ad9..cdd4ec69 100644 --- a/frontend/src/config/import-export.config.tsx +++ b/frontend/src/config/import-export.config.tsx @@ -161,7 +161,7 @@ export function useImportExport(
{filename === 'keywords' && (- Expected columns: keyword, volume, difficulty, intent, status + Expected columns: keyword, volume, difficulty, country, status
)} diff --git a/frontend/src/config/pages/keywords.config.tsx b/frontend/src/config/pages/keywords.config.tsx index 17df873b..da9ecbc1 100644 --- a/frontend/src/config/pages/keywords.config.tsx +++ b/frontend/src/config/pages/keywords.config.tsx @@ -11,7 +11,7 @@ import { keywordColumn, volumeColumn, difficultyColumn, - intentColumn, + countryColumn, clusterColumn, sectorColumn, statusColumn, @@ -106,7 +106,7 @@ export const createKeywordsPageConfig = ( keyword?: string; volume?: number | null; difficulty?: number | null; - intent?: string; + country?: string; cluster_id?: number | null; status: string; }; @@ -116,8 +116,8 @@ export const createKeywordsPageConfig = ( setSearchTerm: (value: string) => void; statusFilter: string; setStatusFilter: (value: string) => void; - intentFilter: string; - setIntentFilter: (value: string) => void; + countryFilter: string; + setCountryFilter: (value: string) => void; difficultyFilter: string; setDifficultyFilter: (value: string) => void; clusterFilter: string; @@ -197,28 +197,23 @@ export const createKeywordsPageConfig = ( }, }, { - ...intentColumn, - sortable: false, // Backend doesn't support sorting by intent - sortField: 'seed_keyword__intent', + ...countryColumn, + sortable: false, // Backend doesn't support sorting by country + sortField: 'seed_keyword__country', render: (value: string) => { - // Map intent values to badge colors - // Transactional and Commercial → success (green, like active) - // Navigational → warning (amber/yellow, like pending) - // Informational → info (blue) - const getIntentColor = (intent: string) => { - const lowerIntent = intent?.toLowerCase() || ''; - if (lowerIntent === 'transactional' || lowerIntent === 'commercial') { - return 'success'; // Green, like active status - } else if (lowerIntent === 'navigational') { - return 'warning'; // Amber/yellow, like pending status - } - return 'info'; // Blue for informational or default + const countryNames: Record- Expected columns: keyword, volume, difficulty, intent, industry_name, sector_name + Expected columns: keyword, volume, difficulty, country, industry_name, sector_name