.vbw-planning/milestones/ui-fixes-and-smart-scraping/phases/04-smart-scrape-recommendations/04-PLAN-03.md
Add a visual indicator badge on source rows in the sources index that flags sources as scrape candidates (low avg feed word count, scraping not enabled). Uses pre-computed data already available in the controller.
Files:
app/controllers/source_monitor/sources_controller.rbDescription:
In the index action, after computing @avg_feed_word_counts, compute the set of scrape candidate source IDs for the current page. Use the already-loaded @avg_feed_word_counts hash and threshold config to determine candidates without extra queries:
@scrape_recommendation_threshold = SourceMonitor.config.scraping.scrape_recommendation_threshold
@scrape_candidate_ids = compute_scrape_candidate_ids(source_ids)
Add private method:
def compute_scrape_candidate_ids(source_ids)
return Set.new if source_ids.empty?
threshold = SourceMonitor.config.scraping.scrape_recommendation_threshold
return Set.new if threshold.nil? || threshold <= 0
# Use already-loaded avg_feed_word_counts + check scraping_enabled from loaded sources
candidate_ids = @sources.select do |source|
avg = @avg_feed_word_counts[source.id]
avg.present? && avg < threshold && !source.scraping_enabled?
end.map(&:id)
Set.new(candidate_ids)
end
Pass scrape_candidate_ids to the row partial via locals.
Tests:
test/controllers/source_monitor/sources_controller_test.rb -- Add test for:
@scrape_candidate_ids is assigned as a SetFiles:
app/views/source_monitor/sources/_row.html.erbDescription:
Add a badge next to the source name (after the existing "Blocked" badge) when the source is a scrape candidate. Use the scrape_candidate_ids local:
<% scrape_candidates = local_assigns[:scrape_candidate_ids] || Set.new %>
<% if scrape_candidates.include?(source.id) %>
<span class="inline-flex items-center rounded-full bg-violet-100 px-2 py-0.5 text-[10px] font-semibold text-violet-700"
title="Low feed word count — consider enabling scraping"
data-testid="scrape-recommendation-badge">
Scrape Recommended
</span>
<% end %>
Place this badge in the existing flex items-center gap-2 div alongside the source name and blocked badge.
Tests:
test/system/sources_test.rb or integration test -- Assert badge appears for candidate sources and not for non-candidatesFiles:
app/views/source_monitor/sources/index.html.erbDescription:
Update the render partial: "source_monitor/sources/row" call to include scrape_candidate_ids in locals:
<%= render partial: "source_monitor/sources/row",
collection: @sources,
as: :source,
locals: {
item_activity_rates: @item_activity_rates,
avg_feed_word_counts: @avg_feed_word_counts,
avg_scraped_word_counts: @avg_scraped_word_counts,
search_params: @search_params,
scrape_candidate_ids: @scrape_candidate_ids
} %>
Tests:
Files:
app/views/source_monitor/sources/index.html.erbDescription:
When the dashboard widget links to sources with avg_feed_words_lt filter, the sources index should display a filter banner pill showing the active filter. Add avg_feed_words_lt to the dropdown_filter_keys array and add a filter label for it:
In the filter_labels hash, add:
"avg_feed_words_lt" => "Avg Feed Words: < #{@search_params['avg_feed_words_lt']}"
Also add to the dropdown_filter_keys array so it shows as an active filter pill.
Tests:
q[avg_feed_words_lt]=200 and assert filter pill is rendered