solidstats 2.0.0 → 3.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 +12 -63
- data/README.md +27 -0
- data/Rakefile +3 -3
- data/app/assets/javascripts/solidstats/dashboard.js +0 -46
- data/app/assets/stylesheets/solidstats/dashboard.css +48 -0
- data/app/controllers/solidstats/dashboard_controller.rb +73 -61
- data/app/controllers/solidstats/logs_controller.rb +72 -0
- data/app/controllers/solidstats/performance_controller.rb +25 -0
- data/app/controllers/solidstats/productivity_controller.rb +39 -0
- data/app/controllers/solidstats/quality_controller.rb +152 -0
- data/app/controllers/solidstats/securities_controller.rb +30 -0
- data/app/helpers/solidstats/application_helper.rb +124 -11
- data/app/helpers/solidstats/performance_helper.rb +87 -0
- data/app/helpers/solidstats/productivity_helper.rb +38 -0
- data/app/services/solidstats/bundler_audit_service.rb +206 -0
- data/app/services/solidstats/coverage_compass_service.rb +335 -0
- data/app/services/solidstats/load_lens_service.rb +474 -0
- data/app/services/solidstats/log_size_monitor_service.rb +197 -66
- data/app/services/solidstats/my_todo_service.rb +242 -0
- data/app/services/solidstats/style_patrol_service.rb +319 -0
- data/app/views/layouts/solidstats/application.html.erb +8 -2
- data/app/views/layouts/solidstats/dashboard.html.erb +84 -0
- data/app/views/solidstats/dashboard/dashboard.html.erb +39 -0
- data/app/views/solidstats/logs/logs_size.html.erb +409 -0
- data/app/views/solidstats/performance/load_lens.html.erb +158 -0
- data/app/views/solidstats/productivity/_todo_list.html.erb +49 -0
- data/app/views/solidstats/productivity/my_todos.html.erb +84 -0
- data/app/views/solidstats/quality/coverage_compass.html.erb +420 -0
- data/app/views/solidstats/quality/style_patrol.html.erb +463 -0
- data/app/views/solidstats/securities/bundler_audit.html.erb +345 -0
- data/app/views/solidstats/shared/_dashboard_card.html.erb +160 -0
- data/app/views/solidstats/shared/_quick_actions.html.erb +26 -0
- data/config/routes.rb +31 -6
- data/lib/generators/solidstats/clean/clean_generator.rb +24 -0
- data/lib/generators/solidstats/clean/templates/README +8 -0
- data/lib/generators/solidstats/install/install_generator.rb +51 -10
- data/lib/generators/solidstats/install/templates/README +7 -0
- data/lib/solidstats/engine.rb +6 -71
- data/lib/solidstats/version.rb +1 -1
- data/lib/solidstats.rb +19 -303
- data/lib/tasks/solidstats.rake +67 -0
- data/lib/tasks/solidstats_performance.rake +61 -0
- data/lib/tasks/solidstats_tasks.rake +16 -4
- metadata +33 -95
- data/app/assets/javascripts/solidstats/gem_metadata.js +0 -554
- data/app/assets/stylesheets/solidstats/components/action_button.css +0 -99
- data/app/assets/stylesheets/solidstats/components/dashboard.css +0 -151
- data/app/assets/stylesheets/solidstats/components/dashboard_header.css +0 -93
- data/app/assets/stylesheets/solidstats/components/dashboard_layout.css +0 -97
- data/app/assets/stylesheets/solidstats/components/gem_metadata.css +0 -1403
- data/app/assets/stylesheets/solidstats/components/navigation.css +0 -80
- data/app/assets/stylesheets/solidstats/components/quick_navigation.css +0 -54
- data/app/assets/stylesheets/solidstats/components/security.css +0 -332
- data/app/assets/stylesheets/solidstats/components/status_badge.css +0 -58
- data/app/assets/stylesheets/solidstats/components/summary_card.css +0 -66
- data/app/assets/stylesheets/solidstats/components/tab_navigation.css +0 -95
- data/app/components/solidstats/base_component.rb +0 -88
- 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 +0 -45
- data/app/components/solidstats/code_quality/section_component.rb +0 -34
- data/app/components/solidstats/dashboard_header_component.html.erb +0 -39
- data/app/components/solidstats/dashboard_header_component.rb +0 -33
- data/app/components/solidstats/previews/action_button_component_preview/button_vs_link.html.erb +0 -6
- data/app/components/solidstats/previews/action_button_component_preview/sizes.html.erb +0 -6
- data/app/components/solidstats/previews/action_button_component_preview/variants.html.erb +0 -6
- data/app/components/solidstats/previews/action_button_component_preview/with_icons.html.erb +0 -6
- data/app/components/solidstats/previews/action_button_component_preview.rb +0 -64
- data/app/components/solidstats/previews/navigation_component_preview.rb +0 -74
- data/app/components/solidstats/previews/stats_overview_component_preview.rb +0 -100
- data/app/components/solidstats/previews/status_badge_component_preview/sizes.html.erb +0 -6
- data/app/components/solidstats/previews/status_badge_component_preview/statuses.html.erb +0 -6
- data/app/components/solidstats/previews/status_badge_component_preview/with_icons.html.erb +0 -6
- data/app/components/solidstats/previews/status_badge_component_preview.rb +0 -49
- data/app/components/solidstats/previews/summary_card_component_preview/clickable.html.erb +0 -9
- data/app/components/solidstats/previews/summary_card_component_preview/dashboard_layout.html.erb +0 -9
- data/app/components/solidstats/previews/summary_card_component_preview/statuses.html.erb +0 -6
- data/app/components/solidstats/previews/summary_card_component_preview/value_formats.html.erb +0 -6
- data/app/components/solidstats/previews/summary_card_component_preview.rb +0 -67
- data/app/components/solidstats/quick_navigation_component.html.erb +0 -8
- data/app/components/solidstats/quick_navigation_component.rb +0 -21
- data/app/components/solidstats/security/gem_impact_analysis_component.html.erb +0 -44
- data/app/components/solidstats/security/gem_impact_analysis_component.rb +0 -45
- data/app/components/solidstats/security/overview_component.html.erb +0 -21
- data/app/components/solidstats/security/overview_component.rb +0 -104
- data/app/components/solidstats/security/section_component.html.erb +0 -26
- data/app/components/solidstats/security/section_component.rb +0 -52
- data/app/components/solidstats/security/timeline_component.html.erb +0 -39
- data/app/components/solidstats/security/timeline_component.rb +0 -43
- data/app/components/solidstats/tasks_section_component.html.erb +0 -17
- data/app/components/solidstats/tasks_section_component.rb +0 -22
- data/app/components/solidstats/ui/action_button_component.html.erb +0 -6
- data/app/components/solidstats/ui/action_button_component.rb +0 -71
- data/app/components/solidstats/ui/dashboard_layout_component.html.erb +0 -19
- data/app/components/solidstats/ui/dashboard_layout_component.rb +0 -85
- data/app/components/solidstats/ui/navigation_component.html.erb +0 -34
- data/app/components/solidstats/ui/navigation_component.rb +0 -72
- data/app/components/solidstats/ui/stats_overview_component.html.erb +0 -14
- data/app/components/solidstats/ui/stats_overview_component.rb +0 -78
- data/app/components/solidstats/ui/status_badge_component.html.erb +0 -6
- data/app/components/solidstats/ui/status_badge_component.rb +0 -42
- data/app/components/solidstats/ui/summary_card_component.html.erb +0 -12
- data/app/components/solidstats/ui/summary_card_component.rb +0 -63
- data/app/components/solidstats/ui/tab_navigation_component.html.erb +0 -22
- data/app/components/solidstats/ui/tab_navigation_component.rb +0 -79
- data/app/services/solidstats/audit_service.rb +0 -56
- data/app/services/solidstats/data_collector_service.rb +0 -83
- data/app/services/solidstats/gem_metadata/fetcher_service.rb +0 -136
- data/app/services/solidstats/todo_service.rb +0 -114
- data/app/views/solidstats/dashboard/_log_monitor.html.erb +0 -759
- data/app/views/solidstats/dashboard/_todos.html.erb +0 -151
- data/app/views/solidstats/dashboard/audit/_additional_styles.css +0 -22
- data/app/views/solidstats/dashboard/audit/_audit_badge.html.erb +0 -5
- data/app/views/solidstats/dashboard/audit/_audit_details.html.erb +0 -495
- data/app/views/solidstats/dashboard/audit/_audit_summary.html.erb +0 -26
- data/app/views/solidstats/dashboard/audit/_no_vulnerabilities.html.erb +0 -3
- data/app/views/solidstats/dashboard/audit/_security_audit.html.erb +0 -14
- data/app/views/solidstats/dashboard/audit/_vulnerabilities_table.html.erb +0 -1120
- data/app/views/solidstats/dashboard/audit/_vulnerability_details.html.erb +0 -63
- data/app/views/solidstats/dashboard/index.html.erb +0 -81
- data/app/views/solidstats/gem_metadata/_panel.html.erb +0 -419
- data/lib/generators/solidstats/feature/feature_generator.rb +0 -170
- data/lib/generators/solidstats/feature/templates/component.html.erb +0 -84
- data/lib/generators/solidstats/feature/templates/component.rb.erb +0 -103
- data/lib/generators/solidstats/feature/templates/component.scss +0 -243
- data/lib/generators/solidstats/feature/templates/component_test.rb.erb +0 -183
- data/lib/generators/solidstats/feature/templates/controller.rb.erb +0 -44
- data/lib/generators/solidstats/feature/templates/controller_test.rb.erb +0 -111
- data/lib/generators/solidstats/feature/templates/detail_view.html.erb +0 -755
- data/lib/generators/solidstats/feature/templates/preview.rb.erb +0 -107
- data/lib/generators/solidstats/feature/templates/service.rb.erb +0 -132
- data/lib/generators/solidstats/feature/templates/service_test.rb.erb +0 -109
- data/lib/generators/solidstats/install_generator.rb +0 -109
- data/lib/tasks/solidstats_install.rake +0 -133
@@ -1,85 +0,0 @@
|
|
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
|
@@ -1,34 +0,0 @@
|
|
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 %>
|
@@ -1,72 +0,0 @@
|
|
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
|
@@ -1,14 +0,0 @@
|
|
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 %>
|
@@ -1,78 +0,0 @@
|
|
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
|
@@ -1,42 +0,0 @@
|
|
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
|
@@ -1,12 +0,0 @@
|
|
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 %>
|
@@ -1,63 +0,0 @@
|
|
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
|
@@ -1,22 +0,0 @@
|
|
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 %>
|
@@ -1,79 +0,0 @@
|
|
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
|
@@ -1,56 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Solidstats
|
4
|
-
# Service to collect and process security audit data
|
5
|
-
class AuditService < DataCollectorService
|
6
|
-
# Initialize with default cache settings for audits
|
7
|
-
def initialize
|
8
|
-
super(Rails.root.join("tmp", "solidstats_audit.json"))
|
9
|
-
end
|
10
|
-
|
11
|
-
# Generate a summary for the dashboard display
|
12
|
-
# @return [Hash] Summary information with status, count, and message
|
13
|
-
def summary
|
14
|
-
data = fetch
|
15
|
-
vuln_count = data["vulnerabilities"]&.count || 0
|
16
|
-
|
17
|
-
{
|
18
|
-
count: vuln_count,
|
19
|
-
status: determine_status(vuln_count),
|
20
|
-
message: generate_message(vuln_count)
|
21
|
-
}
|
22
|
-
end
|
23
|
-
|
24
|
-
private
|
25
|
-
|
26
|
-
# Determine the status indicator based on vulnerability count
|
27
|
-
# @param count [Integer] Number of vulnerabilities
|
28
|
-
# @return [String] Status indicator (success, warning, or danger)
|
29
|
-
def determine_status(count)
|
30
|
-
case count
|
31
|
-
when 0 then "success"
|
32
|
-
when 1..2 then "warning"
|
33
|
-
else "danger"
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
# Generate a status message based on vulnerability count
|
38
|
-
# @param count [Integer] Number of vulnerabilities
|
39
|
-
# @return [String] Human-readable status message
|
40
|
-
def generate_message(count)
|
41
|
-
case count
|
42
|
-
when 0 then "No vulnerabilities found"
|
43
|
-
when 1 then "1 vulnerability found"
|
44
|
-
else "#{count} vulnerabilities found"
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
# Collect fresh audit data by running bundle-audit
|
49
|
-
# @return [Hash] Parsed audit data
|
50
|
-
def collect_data
|
51
|
-
raw_output = `bundle audit check --update --format json`
|
52
|
-
json_part = raw_output[/\{.*\}/m] # extract JSON starting from first '{'
|
53
|
-
JSON.parse(json_part) rescue { "error" => "Failed to parse JSON", "vulnerabilities" => [] }
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
@@ -1,83 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Solidstats
|
4
|
-
# Base service class for all data collectors
|
5
|
-
# This class handles caching logic and provides a common interface
|
6
|
-
# for all data collection services in SolidStats
|
7
|
-
class DataCollectorService
|
8
|
-
attr_reader :cache_file, :cache_duration
|
9
|
-
|
10
|
-
# Initialize the data collector with cache configuration
|
11
|
-
# @param cache_file [String] Path to the cache file
|
12
|
-
# @param cache_duration [ActiveSupport::Duration] Duration to keep the cache valid
|
13
|
-
def initialize(cache_file, cache_duration = 12.hours)
|
14
|
-
@cache_file = cache_file
|
15
|
-
@cache_duration = cache_duration
|
16
|
-
end
|
17
|
-
|
18
|
-
# Fetch data with caching
|
19
|
-
# Returns cached data if available and not expired,
|
20
|
-
# otherwise collects fresh data
|
21
|
-
# @param force_refresh [Boolean] Whether to force a refresh regardless of cache state
|
22
|
-
# @return [Hash] The collected data
|
23
|
-
def fetch(force_refresh = false)
|
24
|
-
return cached_data if fresh_cache? && !force_refresh
|
25
|
-
|
26
|
-
data = collect_data
|
27
|
-
save_to_cache(data)
|
28
|
-
data
|
29
|
-
end
|
30
|
-
|
31
|
-
# Get a summary of the data for dashboard display
|
32
|
-
# @return [Hash] Summary information
|
33
|
-
def summary
|
34
|
-
raise NotImplementedError, "Subclasses must implement summary"
|
35
|
-
end
|
36
|
-
|
37
|
-
private
|
38
|
-
|
39
|
-
# Get data from the cache file
|
40
|
-
# @return [Hash, nil] Cached data or nil if cache invalid
|
41
|
-
def cached_data
|
42
|
-
JSON.parse(File.read(cache_file))["output"]
|
43
|
-
rescue StandardError => e
|
44
|
-
Rails.logger.error "Error reading cache: #{e.message}"
|
45
|
-
nil
|
46
|
-
end
|
47
|
-
|
48
|
-
# Check if the cache is fresh (exists and not expired)
|
49
|
-
# @return [Boolean] True if cache is valid and not expired
|
50
|
-
def fresh_cache?
|
51
|
-
return false unless File.exist?(cache_file)
|
52
|
-
|
53
|
-
begin
|
54
|
-
cached_data = JSON.parse(File.read(cache_file))
|
55
|
-
last_run_time = Time.parse(cached_data["timestamp"])
|
56
|
-
Time.now - last_run_time < cache_duration
|
57
|
-
rescue StandardError => e
|
58
|
-
Rails.logger.error "Error checking cache freshness: #{e.message}"
|
59
|
-
false
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
# Save data to the cache file
|
64
|
-
# @param data [Hash] Data to save to cache
|
65
|
-
def save_to_cache(data)
|
66
|
-
cache_data = {
|
67
|
-
"output" => data,
|
68
|
-
"timestamp" => Time.now.iso8601
|
69
|
-
}
|
70
|
-
|
71
|
-
FileUtils.mkdir_p(File.dirname(cache_file))
|
72
|
-
File.write(cache_file, JSON.generate(cache_data))
|
73
|
-
rescue StandardError => e
|
74
|
-
Rails.logger.error "Error saving to cache: #{e.message}"
|
75
|
-
end
|
76
|
-
|
77
|
-
# Collect fresh data - to be implemented by subclasses
|
78
|
-
# @return [Hash] The collected data
|
79
|
-
def collect_data
|
80
|
-
raise NotImplementedError, "Subclasses must implement collect_data"
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|