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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +68 -0
- data/README.md +33 -0
- data/app/assets/javascripts/solidstats/application.js +257 -0
- data/app/assets/javascripts/solidstats/dashboard.js +225 -0
- data/app/assets/javascripts/solidstats/gem_metadata.js +554 -0
- data/app/assets/stylesheets/solidstats/application.css +6 -1
- data/app/assets/stylesheets/solidstats/components/action_button.css +99 -0
- data/app/assets/stylesheets/solidstats/components/dashboard.css +151 -0
- data/app/assets/stylesheets/solidstats/components/dashboard_header.css +93 -0
- data/app/assets/stylesheets/solidstats/components/dashboard_layout.css +97 -0
- data/app/assets/stylesheets/solidstats/components/gem_metadata.css +1403 -0
- data/app/assets/stylesheets/solidstats/components/navigation.css +80 -0
- data/app/assets/stylesheets/solidstats/components/quick_navigation.css +54 -0
- data/app/assets/stylesheets/solidstats/components/security.css +332 -0
- data/app/assets/stylesheets/solidstats/components/status_badge.css +58 -0
- data/app/assets/stylesheets/solidstats/components/summary_card.css +66 -0
- data/app/assets/stylesheets/solidstats/components/tab_navigation.css +95 -0
- data/app/components/solidstats/base_component.rb +88 -0
- data/app/components/solidstats/code_quality/code_quality_section_component.html.erb +0 -0
- data/app/components/solidstats/code_quality/code_quality_section_component.rb +0 -0
- data/app/components/solidstats/code_quality/section_component.html.erb +45 -0
- data/app/components/solidstats/code_quality/section_component.rb +34 -0
- data/app/components/solidstats/dashboard_header_component.html.erb +39 -0
- data/app/components/solidstats/dashboard_header_component.rb +33 -0
- data/app/components/solidstats/previews/action_button_component_preview/button_vs_link.html.erb +6 -0
- data/app/components/solidstats/previews/action_button_component_preview/sizes.html.erb +6 -0
- data/app/components/solidstats/previews/action_button_component_preview/variants.html.erb +6 -0
- data/app/components/solidstats/previews/action_button_component_preview/with_icons.html.erb +6 -0
- data/app/components/solidstats/previews/action_button_component_preview.rb +64 -0
- data/app/components/solidstats/previews/navigation_component_preview.rb +74 -0
- data/app/components/solidstats/previews/stats_overview_component_preview.rb +100 -0
- data/app/components/solidstats/previews/status_badge_component_preview/sizes.html.erb +6 -0
- data/app/components/solidstats/previews/status_badge_component_preview/statuses.html.erb +6 -0
- data/app/components/solidstats/previews/status_badge_component_preview/with_icons.html.erb +6 -0
- data/app/components/solidstats/previews/status_badge_component_preview.rb +49 -0
- data/app/components/solidstats/previews/summary_card_component_preview/clickable.html.erb +9 -0
- data/app/components/solidstats/previews/summary_card_component_preview/dashboard_layout.html.erb +9 -0
- data/app/components/solidstats/previews/summary_card_component_preview/statuses.html.erb +6 -0
- data/app/components/solidstats/previews/summary_card_component_preview/value_formats.html.erb +6 -0
- data/app/components/solidstats/previews/summary_card_component_preview.rb +67 -0
- data/app/components/solidstats/quick_navigation_component.html.erb +8 -0
- data/app/components/solidstats/quick_navigation_component.rb +21 -0
- data/app/components/solidstats/security/gem_impact_analysis_component.html.erb +44 -0
- data/app/components/solidstats/security/gem_impact_analysis_component.rb +45 -0
- data/app/components/solidstats/security/overview_component.html.erb +21 -0
- data/app/components/solidstats/security/overview_component.rb +104 -0
- data/app/components/solidstats/security/section_component.html.erb +26 -0
- data/app/components/solidstats/security/section_component.rb +52 -0
- data/app/components/solidstats/security/timeline_component.html.erb +39 -0
- data/app/components/solidstats/security/timeline_component.rb +43 -0
- data/app/components/solidstats/tasks_section_component.html.erb +17 -0
- data/app/components/solidstats/tasks_section_component.rb +22 -0
- data/app/components/solidstats/ui/action_button_component.html.erb +6 -0
- data/app/components/solidstats/ui/action_button_component.rb +71 -0
- data/app/components/solidstats/ui/dashboard_layout_component.html.erb +19 -0
- data/app/components/solidstats/ui/dashboard_layout_component.rb +85 -0
- data/app/components/solidstats/ui/navigation_component.html.erb +34 -0
- data/app/components/solidstats/ui/navigation_component.rb +72 -0
- data/app/components/solidstats/ui/stats_overview_component.html.erb +14 -0
- data/app/components/solidstats/ui/stats_overview_component.rb +78 -0
- data/app/components/solidstats/ui/status_badge_component.html.erb +6 -0
- data/app/components/solidstats/ui/status_badge_component.rb +42 -0
- data/app/components/solidstats/ui/summary_card_component.html.erb +12 -0
- data/app/components/solidstats/ui/summary_card_component.rb +63 -0
- data/app/components/solidstats/ui/tab_navigation_component.html.erb +22 -0
- data/app/components/solidstats/ui/tab_navigation_component.rb +79 -0
- data/app/controllers/solidstats/dashboard_controller.rb +8 -5
- data/app/controllers/solidstats/gem_metadata_controller.rb +12 -0
- data/app/helpers/solidstats/application_helper.rb +42 -0
- data/app/services/solidstats/gem_metadata/fetcher_service.rb +136 -0
- data/app/services/solidstats/log_size_monitor_service.rb +10 -10
- data/app/views/layouts/solidstats/application.html.erb +2 -1
- data/app/views/solidstats/dashboard/index.html.erb +67 -1337
- data/app/views/solidstats/gem_metadata/_panel.html.erb +419 -0
- data/config/routes.rb +5 -2
- data/lib/generators/solidstats/feature/feature_generator.rb +170 -0
- data/lib/generators/solidstats/feature/templates/component.html.erb +84 -0
- data/lib/generators/solidstats/feature/templates/component.rb.erb +103 -0
- data/lib/generators/solidstats/feature/templates/component.scss +243 -0
- data/lib/generators/solidstats/feature/templates/component_test.rb.erb +183 -0
- data/lib/generators/solidstats/feature/templates/controller.rb.erb +44 -0
- data/lib/generators/solidstats/feature/templates/controller_test.rb.erb +111 -0
- data/lib/generators/solidstats/feature/templates/detail_view.html.erb +755 -0
- data/lib/generators/solidstats/feature/templates/preview.rb.erb +107 -0
- data/lib/generators/solidstats/feature/templates/service.rb.erb +132 -0
- data/lib/generators/solidstats/feature/templates/service_test.rb.erb +109 -0
- data/lib/generators/solidstats/install_generator.rb +109 -0
- data/lib/generators/solidstats/templates/initializer.rb +112 -0
- data/lib/solidstats/asset_compatibility.rb +238 -0
- data/lib/solidstats/asset_manifest.rb +205 -0
- data/lib/solidstats/engine.rb +114 -9
- data/lib/solidstats/version.rb +1 -1
- data/lib/solidstats.rb +299 -2
- data/lib/tasks/solidstats_install.rake +122 -2
- 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,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?(
|
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
|