.vbw-planning/milestones/07-rails-audit-and-refactoring/05-view-layer-extraction/04-PLAN.md
Create an IconComponent to centralize inline SVG repetition across 5+ files (V9), and split dashboard stat updates into per-stat Turbo Streams for targeted re-rendering (V4). This plan runs in wave 2 because it modifies _details.html.erb (changed by Plan 01) and _row.html.erb/_health_status_badge.html.erb (changed by Plan 03).
_row.html.erb (menu dots), _details.html.erb (refresh), _health_status_badge.html.erb (chevron down), application_helper.rb (external link, spinner)app/views/source_monitor/dashboard/_stats.html.erb re-renders all 5 stat cards on any change (V4)lib/source_monitor/dashboard/turbo_broadcaster.rb broadcasts full stats partialCreate test/components/source_monitor/icon_component_test.rb:
:sm -> "h-4 w-4", :md -> "h-5 w-5", :lg -> "h-6 w-6":md sizearia-hidden="true" attributeCreate app/components/source_monitor/icon_component.rb:
initialize(name, size: :md, css_class: nil)call renders full SVG tag with icon path, size classes, aria-hidden, and optional extra CSS classes:menu_dots, :refresh, :chevron_down, :external_link, :spinnerModify app/views/source_monitor/sources/_row.html.erb:
render SourceMonitor::IconComponent.new(:menu_dots)Modify app/views/source_monitor/sources/_details.html.erb:
render SourceMonitor::IconComponent.new(:refresh, size: :sm)Modify app/views/source_monitor/sources/_health_status_badge.html.erb:
render SourceMonitor::IconComponent.new(:chevron_down, size: :sm)Modify app/helpers/source_monitor/application_helper.rb:
loading_spinner_svg to delegate to render SourceMonitor::IconComponent.new(:spinner, size: :md) or keep as helper that calls the componentexternal_link_icon (private) to use IconComponent internallyModify app/views/source_monitor/dashboard/_stats.html.erb:
source_monitor_stat_total_sources, source_monitor_stat_active_sources, source_monitor_stat_failed_sources, etc._stats.html.erbModify app/views/source_monitor/dashboard/_stat_card.html.erb:
Modify lib/source_monitor/dashboard/turbo_broadcaster.rb:
_stats partial, broadcast individual stat card updatesturbo_stream.replace "source_monitor_stat_#{key}" for each changed statUpdate or create tests for TurboBroadcaster:
bin/rails test test/components/source_monitor/icon_component_test.rb -- all passbin/rails test test/lib/source_monitor/dashboard/ -- dashboard tests passbin/rails test -- full suite passesbin/rubocop -- zero offensesgrep -c "<svg" app/views/source_monitor/sources/_row.html.erb should show reduced count