COMPLETED KEYWORDS-LIBRARY-REDESIGN-PLAN.md

This commit is contained in:
IGNY8 VPS (Salman)
2026-01-18 22:05:38 +00:00
parent 05bc433c80
commit 328098a48c
6 changed files with 264 additions and 93 deletions

View File

@@ -95,7 +95,7 @@ export default function IndustriesSectorsKeywords() {
const [showNotAddedOnly, setShowNotAddedOnly] = useState(false);
const [volumeMin, setVolumeMin] = useState('');
const [volumeMax, setVolumeMax] = useState('');
const [minWords, setMinWords] = useState<number | undefined>(undefined);
// Dynamic filter options (cascading)
const [countryOptions, setCountryOptions] = useState<FilterOption[] | undefined>(undefined);
const [difficultyOptions, setDifficultyOptions] = useState<FilterOption[] | undefined>(undefined);
@@ -199,9 +199,15 @@ export default function IndustriesSectorsKeywords() {
setLoadingSectorStats(true);
try {
// Get the site's sector industry_sector IDs to filter stats
const siteSectorIds = sectors
.filter(s => s.industry_sector)
.map(s => s.industry_sector as number);
const response = await fetchSectorStats({
industry_id: activeSite.industry,
site_id: activeSite.id,
sector_ids: siteSectorIds.length > 0 ? siteSectorIds : undefined,
});
const allSectors = response.sectors || [];
@@ -219,19 +225,36 @@ export default function IndustriesSectorsKeywords() {
const aggregated: SectorStats = {
total: { count: 0 },
available: { count: 0 },
high_volume: { count: 0, threshold: 10000 },
premium_traffic: { count: 0, threshold: 50000 },
long_tail: { count: 0, threshold: 1000 },
quick_wins: { count: 0, threshold: 1000 },
high_volume: { count: 0, available: 0, threshold: 10000 },
mid_volume: { count: 0, available: 0, threshold: 5000 },
premium_traffic: { count: 0, available: 0, threshold: 50000 },
long_tail: { count: 0, available: 0, threshold: 1000 },
quick_wins: { count: 0, available: 0, threshold: 1000 },
};
allSectors.forEach((sector) => {
aggregated.total.count += sector.stats.total.count;
aggregated.available.count += sector.stats.available.count;
// Aggregate counts AND available counts for each stat type
aggregated.high_volume.count += sector.stats.high_volume.count;
aggregated.high_volume.available = (aggregated.high_volume.available || 0) + (sector.stats.high_volume.available || 0);
if (sector.stats.mid_volume) {
aggregated.mid_volume!.count += sector.stats.mid_volume.count;
aggregated.mid_volume!.available = (aggregated.mid_volume!.available || 0) + (sector.stats.mid_volume.available || 0);
}
aggregated.premium_traffic.count += sector.stats.premium_traffic.count;
aggregated.premium_traffic.available = (aggregated.premium_traffic.available || 0) + (sector.stats.premium_traffic.available || 0);
aggregated.long_tail.count += sector.stats.long_tail.count;
aggregated.long_tail.available = (aggregated.long_tail.available || 0) + (sector.stats.long_tail.available || 0);
aggregated.quick_wins.count += sector.stats.quick_wins.count;
aggregated.quick_wins.available = (aggregated.quick_wins.available || 0) + (sector.stats.quick_wins.available || 0);
// Use first non-zero threshold
if (!aggregated.premium_traffic.threshold) {
aggregated.premium_traffic.threshold = sector.stats.premium_traffic.threshold;
}
@@ -252,7 +275,7 @@ export default function IndustriesSectorsKeywords() {
} finally {
setLoadingSectorStats(false);
}
}, [activeSite, activeSector]);
}, [activeSite, activeSector, sectors]);
// Load sector stats when site/sector changes
useEffect(() => {
@@ -268,6 +291,7 @@ export default function IndustriesSectorsKeywords() {
setCountryFilter('');
setDifficultyFilter('');
setShowNotAddedOnly(false);
setMinWords(undefined);
setCurrentPage(1);
setSelectedIds([]);
setAddedStatActions(new Set());
@@ -276,6 +300,7 @@ export default function IndustriesSectorsKeywords() {
// Reset pagination/selection when sector changes
useEffect(() => {
setActiveStatFilter(null);
setMinWords(undefined);
setCurrentPage(1);
setSelectedIds([]);
setAddedStatActions(new Set());
@@ -298,6 +323,9 @@ export default function IndustriesSectorsKeywords() {
volume_min: volumeMin ? Number(volumeMin) : undefined,
volume_max: volumeMax ? Number(volumeMax) : undefined,
search: searchTerm || undefined,
min_words: minWords,
site_id: showNotAddedOnly ? activeSite.id : undefined,
available_only: showNotAddedOnly,
});
setCountryOptions(options.countries || []);
@@ -310,7 +338,7 @@ export default function IndustriesSectorsKeywords() {
} catch (error) {
console.error('Failed to load filter options:', error);
}
}, [activeSite?.industry, activeSector?.industry_sector, countryFilter, difficultyFilter, volumeMin, volumeMax, searchTerm]);
}, [activeSite?.industry, activeSite?.id, activeSector?.industry_sector, countryFilter, difficultyFilter, volumeMin, volumeMax, searchTerm, minWords, showNotAddedOnly]);
useEffect(() => {
loadFilterOptions();
@@ -382,9 +410,17 @@ export default function IndustriesSectorsKeywords() {
page: currentPage,
};
// Add sector filter if active sector is selected
// Add sector filter - if active sector selected, use its ID; otherwise use all site's sector IDs
if (activeSector && activeSector.industry_sector) {
filters.sector = activeSector.industry_sector;
} else {
// Filter by all of the site's sector IDs
const siteSectorIds = sectors
.filter(s => s.industry_sector)
.map(s => s.industry_sector);
if (siteSectorIds.length > 0) {
filters.sector_ids = siteSectorIds.join(',');
}
}
if (searchTerm) filters.search = searchTerm;
@@ -421,6 +457,11 @@ export default function IndustriesSectorsKeywords() {
}
}
// Apply word count filter (for long-tail keywords)
if (minWords !== undefined) {
filters.min_words = minWords;
}
// Add sorting to API request
if (sortBy && sortDirection) {
const sortPrefix = sortDirection === 'desc' ? '-' : '';
@@ -474,7 +515,7 @@ export default function IndustriesSectorsKeywords() {
setAvailableCount(0);
setShowContent(true);
}
}, [activeSite, activeSector, currentPage, pageSize, searchTerm, countryFilter, volumeMin, volumeMax, difficultyFilter, showNotAddedOnly, sortBy, sortDirection, toast]);
}, [activeSite, activeSector, sectors, currentPage, pageSize, searchTerm, countryFilter, volumeMin, volumeMax, difficultyFilter, showNotAddedOnly, minWords, sortBy, sortDirection, toast]);
const getAddedStatStorageKey = useCallback(() => {
if (!activeSite?.id) return null;
@@ -952,9 +993,37 @@ export default function IndustriesSectorsKeywords() {
return `${statType}:${count}`;
}, []);
// Check if a bulk add action should show as "Added"
// This checks actual available counts from backend stats
const isStatActionAdded = useCallback((statType: StatType, count: number) => {
return addedStatActions.has(buildStatActionKey(statType, count));
}, [addedStatActions, buildStatActionKey]);
// First check if it was added in this session (localStorage) - for immediate feedback
if (addedStatActions.has(buildStatActionKey(statType, count))) {
return true;
}
// Check actual available counts from backend stats
// This ensures buttons reflect true state after page reload
if (sectorStats) {
const statData = sectorStats[statType];
if (!statData) return false;
// Get available count - use 'available' field if present, otherwise fallback to count
const availableCount = statData.available !== undefined ? statData.available : statData.count;
// If no keywords available for this stat type, show as Added
if (availableCount === 0) {
return true;
}
// If requesting more than available, mark as added
// (e.g., clicking "+50" when only 30 are available)
if (count > availableCount) {
return true;
}
}
return false;
}, [addedStatActions, buildStatActionKey, sectorStats]);
const fetchBulkKeywords = useCallback(async (options: {
ordering: string;
@@ -1026,6 +1095,7 @@ export default function IndustriesSectorsKeywords() {
setDifficultyFilter('');
setVolumeMin('');
setVolumeMax('');
setMinWords(undefined);
setSelectedIds([]);
return;
}
@@ -1047,12 +1117,14 @@ export default function IndustriesSectorsKeywords() {
setDifficultyFilter('');
setVolumeMin('');
setVolumeMax('');
setMinWords(undefined);
break;
case 'high_volume':
setShowNotAddedOnly(false);
setDifficultyFilter('');
setVolumeMin(String(statThresholds.highVolume));
setVolumeMax('');
setMinWords(undefined);
setSortBy('volume');
setSortDirection('desc');
break;
@@ -1061,6 +1133,7 @@ export default function IndustriesSectorsKeywords() {
setDifficultyFilter('');
setVolumeMin(String(statThresholds.premium));
setVolumeMax('');
setMinWords(undefined);
setSortBy('volume');
setSortDirection('desc');
break;
@@ -1069,6 +1142,7 @@ export default function IndustriesSectorsKeywords() {
setDifficultyFilter('');
setVolumeMin(String(statThresholds.longTail));
setVolumeMax('');
setMinWords(4); // Long-tail = 4+ words
setSortBy('keyword');
setSortDirection('asc');
break;
@@ -1077,6 +1151,7 @@ export default function IndustriesSectorsKeywords() {
setDifficultyFilter('1');
setVolumeMin(String(statThresholds.quickWins));
setVolumeMax('');
setMinWords(undefined);
setSortBy('difficulty');
setSortDirection('asc');
break;
@@ -1085,6 +1160,7 @@ export default function IndustriesSectorsKeywords() {
setDifficultyFilter('');
setVolumeMin('');
setVolumeMax('');
setMinWords(undefined);
}
}, [activeStatFilter, sectorStats]);
@@ -1362,6 +1438,7 @@ export default function IndustriesSectorsKeywords() {
setVolumeMax('');
setDifficultyFilter('');
setShowNotAddedOnly(false);
setMinWords(undefined);
setActiveStatFilter(null);
setCurrentPage(1);
setSelectedIds([]);