solidstats 1.1.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +68 -0
  3. data/README.md +33 -0
  4. data/app/assets/javascripts/solidstats/application.js +257 -0
  5. data/app/assets/javascripts/solidstats/dashboard.js +225 -0
  6. data/app/assets/javascripts/solidstats/gem_metadata.js +554 -0
  7. data/app/assets/stylesheets/solidstats/application.css +6 -1
  8. data/app/assets/stylesheets/solidstats/components/action_button.css +99 -0
  9. data/app/assets/stylesheets/solidstats/components/dashboard.css +151 -0
  10. data/app/assets/stylesheets/solidstats/components/dashboard_header.css +93 -0
  11. data/app/assets/stylesheets/solidstats/components/dashboard_layout.css +97 -0
  12. data/app/assets/stylesheets/solidstats/components/gem_metadata.css +1403 -0
  13. data/app/assets/stylesheets/solidstats/components/navigation.css +80 -0
  14. data/app/assets/stylesheets/solidstats/components/quick_navigation.css +54 -0
  15. data/app/assets/stylesheets/solidstats/components/security.css +332 -0
  16. data/app/assets/stylesheets/solidstats/components/status_badge.css +58 -0
  17. data/app/assets/stylesheets/solidstats/components/summary_card.css +66 -0
  18. data/app/assets/stylesheets/solidstats/components/tab_navigation.css +95 -0
  19. data/app/components/solidstats/base_component.rb +88 -0
  20. data/app/components/solidstats/code_quality/code_quality_section_component.html.erb +0 -0
  21. data/app/components/solidstats/code_quality/code_quality_section_component.rb +0 -0
  22. data/app/components/solidstats/code_quality/section_component.html.erb +45 -0
  23. data/app/components/solidstats/code_quality/section_component.rb +34 -0
  24. data/app/components/solidstats/dashboard_header_component.html.erb +39 -0
  25. data/app/components/solidstats/dashboard_header_component.rb +33 -0
  26. data/app/components/solidstats/previews/action_button_component_preview/button_vs_link.html.erb +6 -0
  27. data/app/components/solidstats/previews/action_button_component_preview/sizes.html.erb +6 -0
  28. data/app/components/solidstats/previews/action_button_component_preview/variants.html.erb +6 -0
  29. data/app/components/solidstats/previews/action_button_component_preview/with_icons.html.erb +6 -0
  30. data/app/components/solidstats/previews/action_button_component_preview.rb +64 -0
  31. data/app/components/solidstats/previews/navigation_component_preview.rb +74 -0
  32. data/app/components/solidstats/previews/stats_overview_component_preview.rb +100 -0
  33. data/app/components/solidstats/previews/status_badge_component_preview/sizes.html.erb +6 -0
  34. data/app/components/solidstats/previews/status_badge_component_preview/statuses.html.erb +6 -0
  35. data/app/components/solidstats/previews/status_badge_component_preview/with_icons.html.erb +6 -0
  36. data/app/components/solidstats/previews/status_badge_component_preview.rb +49 -0
  37. data/app/components/solidstats/previews/summary_card_component_preview/clickable.html.erb +9 -0
  38. data/app/components/solidstats/previews/summary_card_component_preview/dashboard_layout.html.erb +9 -0
  39. data/app/components/solidstats/previews/summary_card_component_preview/statuses.html.erb +6 -0
  40. data/app/components/solidstats/previews/summary_card_component_preview/value_formats.html.erb +6 -0
  41. data/app/components/solidstats/previews/summary_card_component_preview.rb +67 -0
  42. data/app/components/solidstats/quick_navigation_component.html.erb +8 -0
  43. data/app/components/solidstats/quick_navigation_component.rb +21 -0
  44. data/app/components/solidstats/security/gem_impact_analysis_component.html.erb +44 -0
  45. data/app/components/solidstats/security/gem_impact_analysis_component.rb +45 -0
  46. data/app/components/solidstats/security/overview_component.html.erb +21 -0
  47. data/app/components/solidstats/security/overview_component.rb +104 -0
  48. data/app/components/solidstats/security/section_component.html.erb +26 -0
  49. data/app/components/solidstats/security/section_component.rb +52 -0
  50. data/app/components/solidstats/security/timeline_component.html.erb +39 -0
  51. data/app/components/solidstats/security/timeline_component.rb +43 -0
  52. data/app/components/solidstats/tasks_section_component.html.erb +17 -0
  53. data/app/components/solidstats/tasks_section_component.rb +22 -0
  54. data/app/components/solidstats/ui/action_button_component.html.erb +6 -0
  55. data/app/components/solidstats/ui/action_button_component.rb +71 -0
  56. data/app/components/solidstats/ui/dashboard_layout_component.html.erb +19 -0
  57. data/app/components/solidstats/ui/dashboard_layout_component.rb +85 -0
  58. data/app/components/solidstats/ui/navigation_component.html.erb +34 -0
  59. data/app/components/solidstats/ui/navigation_component.rb +72 -0
  60. data/app/components/solidstats/ui/stats_overview_component.html.erb +14 -0
  61. data/app/components/solidstats/ui/stats_overview_component.rb +78 -0
  62. data/app/components/solidstats/ui/status_badge_component.html.erb +6 -0
  63. data/app/components/solidstats/ui/status_badge_component.rb +42 -0
  64. data/app/components/solidstats/ui/summary_card_component.html.erb +12 -0
  65. data/app/components/solidstats/ui/summary_card_component.rb +63 -0
  66. data/app/components/solidstats/ui/tab_navigation_component.html.erb +22 -0
  67. data/app/components/solidstats/ui/tab_navigation_component.rb +79 -0
  68. data/app/controllers/solidstats/dashboard_controller.rb +8 -5
  69. data/app/controllers/solidstats/gem_metadata_controller.rb +12 -0
  70. data/app/helpers/solidstats/application_helper.rb +42 -0
  71. data/app/services/solidstats/gem_metadata/fetcher_service.rb +136 -0
  72. data/app/services/solidstats/log_size_monitor_service.rb +10 -10
  73. data/app/views/layouts/solidstats/application.html.erb +2 -1
  74. data/app/views/solidstats/dashboard/index.html.erb +67 -1337
  75. data/app/views/solidstats/gem_metadata/_panel.html.erb +419 -0
  76. data/config/routes.rb +5 -2
  77. data/lib/generators/solidstats/feature/feature_generator.rb +170 -0
  78. data/lib/generators/solidstats/feature/templates/component.html.erb +84 -0
  79. data/lib/generators/solidstats/feature/templates/component.rb.erb +103 -0
  80. data/lib/generators/solidstats/feature/templates/component.scss +243 -0
  81. data/lib/generators/solidstats/feature/templates/component_test.rb.erb +183 -0
  82. data/lib/generators/solidstats/feature/templates/controller.rb.erb +44 -0
  83. data/lib/generators/solidstats/feature/templates/controller_test.rb.erb +111 -0
  84. data/lib/generators/solidstats/feature/templates/detail_view.html.erb +755 -0
  85. data/lib/generators/solidstats/feature/templates/preview.rb.erb +107 -0
  86. data/lib/generators/solidstats/feature/templates/service.rb.erb +132 -0
  87. data/lib/generators/solidstats/feature/templates/service_test.rb.erb +109 -0
  88. data/lib/generators/solidstats/install_generator.rb +109 -0
  89. data/lib/generators/solidstats/templates/initializer.rb +112 -0
  90. data/lib/solidstats/asset_compatibility.rb +238 -0
  91. data/lib/solidstats/asset_manifest.rb +205 -0
  92. data/lib/solidstats/engine.rb +114 -9
  93. data/lib/solidstats/version.rb +1 -1
  94. data/lib/solidstats.rb +299 -2
  95. data/lib/tasks/solidstats_install.rake +122 -2
  96. metadata +97 -2
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solidstats
4
+ module Ui
5
+ # Main dashboard layout component
6
+ class DashboardLayoutComponent < Solidstats::BaseComponent
7
+ def initialize(title:, subtitle: nil, navigation: {}, **options)
8
+ @title = title
9
+ @subtitle = subtitle
10
+ @navigation = navigation
11
+ @options = options
12
+ end
13
+
14
+ private
15
+
16
+ attr_reader :title, :subtitle, :navigation, :options
17
+
18
+ def dashboard_classes
19
+ css_classes("solidstats-dashboard", options[:class])
20
+ end
21
+
22
+ def dashboard_attributes
23
+ { class: dashboard_classes }.merge(options.except(:class))
24
+ end
25
+
26
+ def header_classes
27
+ css_classes("dashboard-header")
28
+ end
29
+
30
+ def title_with_icon
31
+ if title.include?("🚥")
32
+ title
33
+ else
34
+ "🚥 #{title}"
35
+ end
36
+ end
37
+
38
+ def formatted_subtitle
39
+ return subtitle if subtitle
40
+
41
+ # Default subtitle with current time
42
+ "Last updated: #{Time.now.strftime('%B %d, %Y at %H:%M')}"
43
+ end
44
+
45
+ def navigation_sections
46
+ navigation[:sections] || default_sections
47
+ end
48
+
49
+ def navigation_actions
50
+ navigation[:actions] || default_actions
51
+ end
52
+
53
+ def current_section
54
+ navigation[:current_section] || "overview"
55
+ end
56
+
57
+ def default_sections
58
+ [
59
+ { id: "overview", label: "Overview", href: "#overview" },
60
+ { id: "security", label: "Security", href: "#security" },
61
+ { id: "code-quality", label: "Code Quality", href: "#code-quality" },
62
+ { id: "tasks", label: "Tasks", href: "#tasks" },
63
+ { id: "gem-metadata", label: "Gem Metadata", href: "#gem-metadata" }
64
+ ]
65
+ end
66
+
67
+ def default_actions
68
+ [
69
+ {
70
+ label: "Refresh",
71
+ icon: "↻",
72
+ href: "#",
73
+ onclick: "refreshAudit(); return false;"
74
+ },
75
+ {
76
+ label: "Export",
77
+ icon: "↓",
78
+ disabled: true,
79
+ disabled_reason: "Export is currently disabled"
80
+ }
81
+ ]
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,34 @@
1
+ <%# filepath: /Users/mezbah/microstartup/infolily_organizer/gems/solidstats/ui/navigation_component.html.erb %>
2
+ <%= tag.nav(**nav_attributes) do %>
3
+ <ul>
4
+ <% sections.each do |section| %>
5
+ <li>
6
+ <%= tag.a(**section_attributes(section)) do %>
7
+ <%= section[:label] %>
8
+ <% end %>
9
+ </li>
10
+ <% end %>
11
+ </ul>
12
+
13
+ <% if actions.any? %>
14
+ <div class="dashboard-actions">
15
+ <% actions.each do |action| %>
16
+ <% if action[:href] %>
17
+ <%= tag.a(**action_attributes(action)) do %>
18
+ <% if action[:icon] %>
19
+ <span class="action-icon"><%= action[:icon] %></span>
20
+ <% end %>
21
+ <%= action[:label] %>
22
+ <% end %>
23
+ <% else %>
24
+ <%= tag.button(**action_attributes(action)) do %>
25
+ <% if action[:icon] %>
26
+ <span class="action-icon"><%= action[:icon] %></span>
27
+ <% end %>
28
+ <%= action[:label] %>
29
+ <% end %>
30
+ <% end %>
31
+ <% end %>
32
+ </div>
33
+ <% end %>
34
+ <% end %>
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solidstats
4
+ module Ui
5
+ # Navigation component for dashboard sections
6
+ class NavigationComponent < Solidstats::BaseComponent
7
+ def initialize(current_section: "overview", sections: [], actions: [], **options)
8
+ @current_section = current_section
9
+ @sections = sections
10
+ @actions = actions
11
+ @options = options
12
+ end
13
+
14
+ private
15
+
16
+ attr_reader :current_section, :sections, :actions, :options
17
+
18
+ def nav_classes
19
+ css_classes("dashboard-nav", options[:class])
20
+ end
21
+
22
+ def nav_attributes
23
+ { class: nav_classes }.merge(options.except(:class))
24
+ end
25
+
26
+ def section_active?(section)
27
+ section[:id] == current_section
28
+ end
29
+
30
+ def section_classes(section)
31
+ css_classes(
32
+ "nav-item",
33
+ section_active?(section) ? "active" : nil
34
+ )
35
+ end
36
+
37
+ def section_attributes(section)
38
+ {
39
+ href: section[:href] || "##{section[:id]}",
40
+ class: section_classes(section),
41
+ "data-section": section[:id]
42
+ }.compact
43
+ end
44
+
45
+ def action_classes(action)
46
+ css_classes(
47
+ "action-button",
48
+ action[:disabled] ? "action-button--disabled" : nil
49
+ )
50
+ end
51
+
52
+ def action_attributes(action)
53
+ attrs = {
54
+ class: action_classes(action),
55
+ href: action[:href]
56
+ }
57
+
58
+ if action[:onclick]
59
+ attrs[:onclick] = action[:onclick]
60
+ end
61
+
62
+ if action[:disabled]
63
+ attrs[:disabled] = true
64
+ attrs[:title] = action[:disabled_reason] if action[:disabled_reason]
65
+ attrs[:style] = "cursor: not-allowed;"
66
+ end
67
+
68
+ attrs.compact
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,14 @@
1
+ <%# filepath: /Users/mezbah/microstartup/infolily_organizer/gems/solidstats/ui/stats_overview_component.html.erb %>
2
+ <%= tag.div(**overview_attributes) do %>
3
+ <% metrics.each do |metric| %>
4
+ <%= render(Solidstats::Ui::SummaryCardComponent.new(
5
+ title: metric[:label],
6
+ value: metric[:value],
7
+ status: metric_status(metric),
8
+ icon: metric[:icon],
9
+ href: metric_href(metric),
10
+ section: metric[:section],
11
+ **metric_data_attributes(metric)
12
+ )) %>
13
+ <% end %>
14
+ <% end %>
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solidstats
4
+ module Ui
5
+ # Stats overview component displaying key dashboard metrics
6
+ class StatsOverviewComponent < Solidstats::BaseComponent
7
+ def initialize(metrics: [], **options)
8
+ @metrics = metrics
9
+ @options = options
10
+ end
11
+
12
+ private
13
+
14
+ attr_reader :metrics, :options
15
+
16
+ def overview_classes
17
+ css_classes("stats-summary", options[:class])
18
+ end
19
+
20
+ def overview_attributes
21
+ { class: overview_classes }.merge(options.except(:class))
22
+ end
23
+
24
+ def metric_status(metric)
25
+ return metric[:status] if metric[:status]
26
+
27
+ # Auto-determine status based on value and thresholds
28
+ if metric[:value].to_i == 0 && metric[:zero_is_good]
29
+ :ok
30
+ elsif metric[:thresholds]
31
+ determine_threshold_status(metric[:value], metric[:thresholds])
32
+ else
33
+ :info
34
+ end
35
+ end
36
+
37
+ def determine_threshold_status(value, thresholds)
38
+ numeric_value = extract_numeric_value(value)
39
+
40
+ if numeric_value >= thresholds[:excellent]
41
+ :ok
42
+ elsif numeric_value >= thresholds[:good]
43
+ :ok
44
+ elsif numeric_value >= thresholds[:warning]
45
+ :warning
46
+ else
47
+ :error
48
+ end
49
+ end
50
+
51
+ def extract_numeric_value(value)
52
+ case value
53
+ when Numeric
54
+ value
55
+ when String
56
+ value.to_f
57
+ else
58
+ 0
59
+ end
60
+ end
61
+
62
+ def metric_href(metric)
63
+ if metric[:section] && metric[:tab]
64
+ "##{metric[:section]}"
65
+ elsif metric[:href]
66
+ metric[:href]
67
+ end
68
+ end
69
+
70
+ def metric_data_attributes(metric)
71
+ attrs = {}
72
+ attrs["data-section"] = metric[:section] if metric[:section]
73
+ attrs["data-tab"] = metric[:tab] if metric[:tab]
74
+ attrs
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,6 @@
1
+ <span class="<%= badge_classes %>">
2
+ <% if show_icon %>
3
+ <span class="status-badge__icon"><%= status_icon(status) %></span>
4
+ <% end %>
5
+ <span class="status-badge__text"><%= badge_text %></span>
6
+ </span>
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solidstats
4
+ module Ui
5
+ # Reusable status badge component for consistent status indicators
6
+ class StatusBadgeComponent < Solidstats::BaseComponent
7
+ def initialize(status:, text: nil, size: :md, show_icon: true)
8
+ @status = status
9
+ @text = text
10
+ @size = size
11
+ @show_icon = show_icon
12
+ end
13
+
14
+ private
15
+
16
+ attr_reader :status, :text, :size, :show_icon
17
+
18
+ def badge_text
19
+ text || status_text(status)
20
+ end
21
+
22
+ def badge_classes
23
+ css_classes(
24
+ "status-badge",
25
+ status_class(status),
26
+ size_class
27
+ )
28
+ end
29
+
30
+ def size_class
31
+ case size
32
+ when :sm
33
+ "status-badge--sm"
34
+ when :lg
35
+ "status-badge--lg"
36
+ else
37
+ "status-badge--md"
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,12 @@
1
+ <%# Modern Summary Card Component Template %>
2
+ <%= tag.send(wrapper_tag, **wrapper_attributes) do %>
3
+ <%= tag.div(**card_attributes) do %>
4
+ <% if card_icon %>
5
+ <div class="card-icon"><%= card_icon %></div>
6
+ <% end %>
7
+ <div class="card-content">
8
+ <div class="card-value"><%= formatted_value %></div>
9
+ <div class="card-label"><%= title %></div>
10
+ </div>
11
+ <% end %>
12
+ <% end %>
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solidstats
4
+ module Ui
5
+ # Reusable summary card component for dashboard metrics
6
+ class SummaryCardComponent < Solidstats::BaseComponent
7
+ def initialize(title:, value:, status: :ok, icon: nil, section: nil, tab: nil, href: nil, **options)
8
+ @title = title
9
+ @value = value
10
+ @status = status
11
+ @icon = icon
12
+ @section = section
13
+ @tab = tab
14
+ @href = href
15
+ @options = options
16
+ end
17
+
18
+ private
19
+
20
+ attr_reader :title, :value, :status, :icon, :section, :tab, :href, :options
21
+
22
+ def card_classes
23
+ css_classes(
24
+ "summary-card",
25
+ status.to_s,
26
+ href ? "summary-card--clickable" : nil
27
+ )
28
+ end
29
+
30
+ def card_attributes
31
+ attrs = {
32
+ class: card_classes,
33
+ data: {
34
+ section: section,
35
+ tab: tab
36
+ }.compact
37
+ }.compact
38
+
39
+ attrs.merge(options)
40
+ end
41
+
42
+ def formatted_value
43
+ format_number(value)
44
+ end
45
+
46
+ def card_icon
47
+ icon || status_icon(status)
48
+ end
49
+
50
+ def wrapper_tag
51
+ href ? :a : :div
52
+ end
53
+
54
+ def wrapper_attributes
55
+ if href
56
+ { href: href }
57
+ else
58
+ {}
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,22 @@
1
+ <%# filepath: /Users/mezbah/microstartup/infolily_organizer/gems/solidstats/ui/tab_navigation_component.html.erb %>
2
+ <%= tag.nav(**tabs_attributes) do %>
3
+ <ul class="tab-list">
4
+ <% tabs.each do |tab| %>
5
+ <li class="tab-list-item">
6
+ <% if tab[:disabled] %>
7
+ <%= tag.span(**tab_attributes(tab)) do %>
8
+ <%= tab[:label] %>
9
+ <%= tab_count_badge(tab) %>
10
+ <%= tab_status_indicator(tab) %>
11
+ <% end %>
12
+ <% else %>
13
+ <%= tag.a(**tab_attributes(tab)) do %>
14
+ <%= tab[:label] %>
15
+ <%= tab_count_badge(tab) %>
16
+ <%= tab_status_indicator(tab) %>
17
+ <% end %>
18
+ <% end %>
19
+ </li>
20
+ <% end %>
21
+ </ul>
22
+ <% end %>
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solidstats
4
+ module Ui
5
+ # Tab navigation component for switching between related content sections
6
+ class TabNavigationComponent < Solidstats::BaseComponent
7
+ def initialize(tabs:, current_tab: nil, section_id: nil, **options)
8
+ @tabs = tabs
9
+ @current_tab = current_tab || tabs.first&.dig(:id)
10
+ @section_id = section_id
11
+ @options = options
12
+ end
13
+
14
+ private
15
+
16
+ attr_reader :tabs, :current_tab, :section_id, :options
17
+
18
+ def tabs_classes
19
+ css_classes("tab-navigation", options[:class])
20
+ end
21
+
22
+ def tabs_attributes
23
+ attrs = { class: tabs_classes }
24
+ attrs["data-section"] = section_id if section_id
25
+ attrs.merge(options.except(:class))
26
+ end
27
+
28
+ def tab_active?(tab)
29
+ tab[:id] == current_tab
30
+ end
31
+
32
+ def tab_classes(tab)
33
+ css_classes(
34
+ "tab-item",
35
+ tab_active?(tab) ? "active" : nil,
36
+ tab[:disabled] ? "disabled" : nil
37
+ )
38
+ end
39
+
40
+ def tab_attributes(tab)
41
+ attrs = {
42
+ class: tab_classes(tab),
43
+ "data-tab": tab[:id]
44
+ }
45
+
46
+ if tab[:disabled]
47
+ attrs[:disabled] = true
48
+ attrs[:title] = tab[:disabled_reason] if tab[:disabled_reason]
49
+ else
50
+ attrs[:href] = tab[:href] || "##{section_id}-#{tab[:id]}"
51
+ end
52
+
53
+ attrs.compact
54
+ end
55
+
56
+ def tab_count_badge(tab)
57
+ return unless tab[:count]
58
+
59
+ count_classes = css_classes(
60
+ "tab-count",
61
+ tab[:count] > 0 ? "has-items" : "empty"
62
+ )
63
+
64
+ content_tag(:span, tab[:count], class: count_classes)
65
+ end
66
+
67
+ def tab_status_indicator(tab)
68
+ return unless tab[:status]
69
+
70
+ status_classes = css_classes(
71
+ "tab-status",
72
+ "tab-status--#{tab[:status]}"
73
+ )
74
+
75
+ content_tag(:span, "", class: status_classes, title: tab[:status_text])
76
+ end
77
+ end
78
+ end
79
+ end
@@ -13,6 +13,7 @@ module Solidstats
13
13
  @audit_output = audit_service.fetch
14
14
  @todo_items = todo_service.fetch
15
15
  @log_data = log_monitor_service.collect_data
16
+ @gems = Solidstats::GemMetadata::FetcherService.call
16
17
 
17
18
  # Get summary data for dashboard cards
18
19
  @audit_summary = audit_service.summary
@@ -34,6 +35,7 @@ module Solidstats
34
35
  audit_output = audit_service.fetch(true) # Force refresh
35
36
  todo_items = todo_service.fetch(true) # Force refresh
36
37
  log_data = log_monitor_service.collect_data
38
+ gem_metadata = GemMetadata::FetcherService.call(nil, true) # Force refresh
37
39
 
38
40
  # Get updated summaries
39
41
  audit_summary = audit_service.summary
@@ -49,6 +51,7 @@ module Solidstats
49
51
  audit_summary: audit_summary,
50
52
  todo_summary: todo_summary,
51
53
  log_data: log_data,
54
+ gem_metadata: gem_metadata,
52
55
  last_updated: last_updated,
53
56
  status: "success"
54
57
  }
@@ -59,18 +62,18 @@ module Solidstats
59
62
  message: "Failed to refresh data: #{e.message}"
60
63
  }, status: :internal_server_error
61
64
  end
62
-
65
+
63
66
  def truncate_log
64
67
  log_monitor_service = LogSizeMonitorService.new
65
68
  filename = params[:filename]
66
-
69
+
67
70
  # Add .log extension if not included in the filename
68
- if filename.present? && !filename.end_with?('.log')
71
+ if filename.present? && !filename.end_with?(".log")
69
72
  filename = "#{filename}.log"
70
73
  end
71
-
74
+
72
75
  result = log_monitor_service.truncate_log(filename)
73
-
76
+
74
77
  render json: result
75
78
  end
76
79
  end
@@ -0,0 +1,12 @@
1
+ module Solidstats
2
+ class GemMetadataController < ApplicationController
3
+ def refresh
4
+ @gems = Solidstats::GemMetadata::FetcherService.call(nil, true)
5
+
6
+ respond_to do |format|
7
+ format.html { redirect_to "/solidstats#gem-metadata", notice: "Gem metadata refreshed successfully." }
8
+ format.json { render json: { gems: @gems } }
9
+ end
10
+ end
11
+ end
12
+ end
@@ -1,4 +1,46 @@
1
1
  module Solidstats
2
2
  module ApplicationHelper
3
+ # Inline CSS helper - reads all CSS files and returns them as a style tag
4
+ def solidstats_styles
5
+ return @solidstats_styles_cache if defined?(@solidstats_styles_cache)
6
+
7
+ begin
8
+ engine_root = Solidstats::Engine.root
9
+ css_files = Dir.glob("#{engine_root}/app/assets/stylesheets/solidstats/components/*.css")
10
+
11
+ combined_css = css_files.map do |file_path|
12
+ "/* === #{File.basename(file_path)} === */\n#{File.read(file_path)}"
13
+ end.join("\n\n")
14
+
15
+ @solidstats_styles_cache = content_tag(:style, combined_css.html_safe, type: "text/css")
16
+ rescue => e
17
+ Rails.logger.error "Solidstats CSS loading error: #{e.message}"
18
+ content_tag(:style, "/* Solidstats CSS loading failed: #{e.message} */", type: "text/css")
19
+ end
20
+ end
21
+
22
+ # Inline JavaScript helper - reads all JS files and returns them as a script tag
23
+ def solidstats_scripts
24
+ return @solidstats_scripts_cache if defined?(@solidstats_scripts_cache)
25
+
26
+ begin
27
+ engine_root = Solidstats::Engine.root
28
+ js_file = "#{engine_root}/app/assets/javascripts/solidstats/application.js"
29
+
30
+ if File.exist?(js_file)
31
+ javascript_content = File.read(js_file)
32
+ # Remove Sprockets directives since we're inlining
33
+ cleaned_js = javascript_content.gsub(/^\/\/=.*$/, "").strip
34
+
35
+ @solidstats_scripts_cache = content_tag(:script, cleaned_js.html_safe, type: "text/javascript")
36
+ else
37
+ Rails.logger.warn "Solidstats JavaScript file not found: #{js_file}"
38
+ content_tag(:script, "console.log('Solidstats JavaScript not found');", type: "text/javascript")
39
+ end
40
+ rescue => e
41
+ Rails.logger.error "Solidstats JavaScript loading error: #{e.message}"
42
+ content_tag(:script, "console.error('Solidstats JavaScript loading failed: #{e.message}');", type: "text/javascript")
43
+ end
44
+ end
3
45
  end
4
46
  end