rails_pulse 0.3.3 → 0.4.0.pre.2
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 +299 -0
- data/README.md +44 -3
- data/app/assets/stylesheets/rails_pulse/application.css +21 -32
- data/app/assets/stylesheets/rails_pulse/components/backtrace.css +238 -0
- data/app/assets/stylesheets/rails_pulse/components/badge.css +32 -0
- data/app/assets/stylesheets/rails_pulse/components/button.css +2 -36
- data/app/assets/stylesheets/rails_pulse/components/card.css +8 -11
- data/app/assets/stylesheets/rails_pulse/components/chart.css +16 -3
- data/app/assets/stylesheets/rails_pulse/components/csp_safe_positioning.css +5 -39
- data/app/assets/stylesheets/rails_pulse/components/dashboard.css +0 -52
- data/app/assets/stylesheets/rails_pulse/components/deployments.css +39 -0
- data/app/assets/stylesheets/rails_pulse/components/dialog.css +0 -5
- data/app/assets/stylesheets/rails_pulse/components/input.css +0 -14
- data/app/assets/stylesheets/rails_pulse/components/layouts.css +0 -20
- data/app/assets/stylesheets/rails_pulse/components/menu.css +0 -16
- data/app/assets/stylesheets/rails_pulse/components/sidebar_menu.css +0 -16
- data/app/assets/stylesheets/rails_pulse/components/storage.css +187 -0
- data/app/assets/stylesheets/rails_pulse/components/tags.css +0 -14
- data/app/assets/stylesheets/rails_pulse/components/time_range_selector.css +5 -5
- data/app/assets/stylesheets/rails_pulse/components/utilities.css +0 -17
- data/app/controllers/concerns/chart_table_concern.rb +15 -3
- data/app/controllers/concerns/ransack_params_concern.rb +18 -0
- data/app/controllers/concerns/response_range_concern.rb +1 -1
- data/app/controllers/concerns/time_range_concern.rb +28 -14
- data/app/controllers/rails_pulse/application_controller.rb +104 -31
- data/app/controllers/rails_pulse/dashboard_controller.rb +9 -0
- data/app/controllers/rails_pulse/deployments_controller.rb +26 -5
- data/app/controllers/rails_pulse/exception_occurrences_controller.rb +19 -0
- data/app/controllers/rails_pulse/exceptions_controller.rb +119 -0
- data/app/controllers/rails_pulse/job_runs_controller.rb +1 -1
- data/app/controllers/rails_pulse/requests_controller.rb +1 -1
- data/app/controllers/rails_pulse/routes_controller.rb +7 -1
- data/app/controllers/rails_pulse/storage_controller.rb +7 -0
- data/app/controllers/rails_pulse/tags_controller.rb +2 -2
- data/app/helpers/rails_pulse/application_helper.rb +1 -0
- data/app/helpers/rails_pulse/backtrace_helper.rb +85 -0
- data/app/helpers/rails_pulse/breadcrumbs_helper.rb +4 -1
- data/app/helpers/rails_pulse/chart_helper.rb +0 -3
- data/app/helpers/rails_pulse/route_helper.rb +8 -16
- data/app/javascript/rails_pulse/application.js +29 -8
- data/app/javascript/rails_pulse/controllers/clipboard_controller.js +58 -0
- data/app/javascript/rails_pulse/controllers/index_controller.js +16 -10
- data/app/javascript/rails_pulse/controllers/pagination_controller.js +6 -2
- data/app/javascript/rails_pulse/controllers/popover_controller.js +4 -0
- data/app/models/concerns/rails_pulse/taggable.rb +11 -6
- data/app/models/rails_pulse/cards/base.rb +53 -0
- data/app/models/rails_pulse/dashboard/health_summary.rb +52 -0
- data/app/models/rails_pulse/dashboard/needs_attention.rb +73 -1
- data/app/models/rails_pulse/dashboard/storage_status.rb +490 -0
- data/app/models/rails_pulse/deployment.rb +47 -1
- data/app/models/rails_pulse/exception_group.rb +37 -0
- data/app/models/rails_pulse/exception_occurrence.rb +27 -0
- data/app/models/rails_pulse/exceptions/cards/exception_rate.rb +116 -0
- data/app/models/rails_pulse/exceptions/cards/open_groups.rb +49 -0
- data/app/models/rails_pulse/exceptions/cards/total_occurrences.rb +62 -0
- data/app/models/rails_pulse/exceptions/charts/occurrence_volume.rb +56 -0
- data/app/models/rails_pulse/exceptions/raw_data.rb +216 -0
- data/app/models/rails_pulse/jobs/cards/p95_duration.rb +8 -2
- data/app/models/rails_pulse/operation.rb +13 -4
- data/app/models/rails_pulse/queries/cards/percentile_query_times.rb +8 -3
- data/app/models/rails_pulse/request.rb +5 -1
- data/app/models/rails_pulse/route.rb +71 -21
- data/app/models/rails_pulse/routes/cards/error_rates.rb +6 -3
- data/app/models/rails_pulse/routes/cards/percentile_response_times.rb +8 -3
- data/app/models/rails_pulse/routes/tables/index.rb +8 -6
- data/app/models/rails_pulse/summary.rb +21 -10
- data/app/models/rails_pulse/tables/base.rb +4 -2
- data/app/services/rails_pulse/analysis/explain_plan_analyzer.rb +47 -12
- data/app/services/rails_pulse/exception_capture_service.rb +228 -0
- data/app/services/rails_pulse/exception_message_sanitizer.rb +152 -0
- data/app/services/rails_pulse/operations/change_point.rb +144 -0
- data/app/services/rails_pulse/operations/compare.rb +92 -0
- data/app/services/rails_pulse/operations/comparison.rb +126 -0
- data/app/services/rails_pulse/operations/metric.rb +63 -0
- data/app/services/rails_pulse/operations/series.rb +84 -0
- data/app/services/rails_pulse/operations/subject.rb +77 -0
- data/app/services/rails_pulse/route_controller_action_backfiller.rb +110 -0
- data/app/services/rails_pulse/route_merger.rb +99 -0
- data/app/services/rails_pulse/route_migrator.rb +139 -0
- data/app/services/rails_pulse/route_path_normalizer.rb +79 -0
- data/app/services/rails_pulse/route_recognizer.rb +47 -0
- data/app/services/rails_pulse/summary_service.rb +45 -0
- data/app/services/rails_pulse/tag_filter_service.rb +5 -3
- data/app/views/layouts/rails_pulse/_menu_items.html.erb +7 -0
- data/app/views/layouts/rails_pulse/_onboarding_banner.html.erb +8 -0
- data/app/views/layouts/rails_pulse/_setup_screen.html.erb +5 -5
- data/app/views/layouts/rails_pulse/application.html.erb +4 -4
- data/app/views/rails_pulse/components/_empty_state.html.erb +1 -1
- data/app/views/rails_pulse/components/_panel.html.erb +16 -6
- data/app/views/rails_pulse/components/_raw_data.html.erb +34 -0
- data/app/views/rails_pulse/dashboard/_chart_panel.html.erb +6 -8
- data/app/views/rails_pulse/dashboard/_deployments_panel.html.erb +64 -0
- data/app/views/rails_pulse/dashboard/_health_badge.html.erb +21 -10
- data/app/views/rails_pulse/dashboard/_storage_panel.html.erb +67 -0
- data/app/views/rails_pulse/dashboard/index.html.erb +22 -1
- data/app/views/rails_pulse/deployments/_status_badge.html.erb +5 -0
- data/app/views/rails_pulse/deployments/_table.html.erb +29 -0
- data/app/views/rails_pulse/deployments/index.html.erb +15 -0
- data/app/views/rails_pulse/deployments/show.html.erb +62 -0
- data/app/views/rails_pulse/exception_occurrences/show.html.erb +45 -0
- data/app/views/rails_pulse/exceptions/_backtrace.html.erb +33 -0
- data/app/views/rails_pulse/exceptions/_metric_cards.html.erb +6 -0
- data/app/views/rails_pulse/exceptions/_occurrences_table.html.erb +23 -0
- data/app/views/rails_pulse/exceptions/_status_badge.html.erb +6 -0
- data/app/views/rails_pulse/exceptions/_table.html.erb +33 -0
- data/app/views/rails_pulse/exceptions/index.html.erb +66 -0
- data/app/views/rails_pulse/exceptions/show.html.erb +52 -0
- data/app/views/rails_pulse/queries/_raw_data.html.erb +1 -23
- data/app/views/rails_pulse/requests/_table.html.erb +1 -1
- data/app/views/rails_pulse/routes/_requests_table.html.erb +5 -0
- data/app/views/rails_pulse/routes/_table.html.erb +22 -8
- data/app/views/rails_pulse/routes/index.html.erb +5 -1
- data/app/views/rails_pulse/routes/show.html.erb +5 -0
- data/app/views/rails_pulse/storage/show.html.erb +123 -0
- data/config/routes.rb +13 -4
- data/db/rails_pulse_migrate/20260506000001_create_rails_pulse_exceptions.rb +50 -0
- data/db/rails_pulse_migrate/20260507000001_add_actual_query_to_operations.rb +10 -5
- data/db/rails_pulse_migrate/20260610000001_add_controller_action_to_rails_pulse_routes.rb +8 -0
- data/db/rails_pulse_migrate/20260610000002_change_rails_pulse_routes_to_multi_verb_model.rb +185 -0
- data/db/rails_pulse_migrate/20260610000003_add_null_action_unique_index_to_routes.rb +32 -0
- data/db/rails_pulse_migrate/20260823000001_add_location_to_exception_groups.rb +12 -0
- data/db/rails_pulse_schema.rb +54 -6
- data/lib/generators/rails_pulse/base_methods.rb +2 -0
- data/lib/generators/rails_pulse/install_generator.rb +5 -0
- data/lib/generators/rails_pulse/schema_parser.rb +22 -2
- data/lib/generators/rails_pulse/templates/db/rails_pulse_schema.rb +54 -6
- data/lib/generators/rails_pulse/templates/migrations/upgrade_rails_pulse_tables.rb +1 -1
- data/lib/generators/rails_pulse/templates/rails_pulse.rb +157 -16
- data/lib/generators/rails_pulse/upgrade_generator.rb +166 -38
- data/lib/rails_pulse/cleanup_service.rb +110 -15
- data/lib/rails_pulse/configuration.rb +125 -6
- data/lib/rails_pulse/engine.rb +32 -17
- data/lib/rails_pulse/installers/config_updater.rb +202 -0
- data/lib/rails_pulse/job_run_collector.rb +43 -16
- data/lib/rails_pulse/middleware/asset_server.rb +11 -0
- data/lib/rails_pulse/middleware/request_collector.rb +9 -4
- data/lib/rails_pulse/packaged_assets.rb +147 -0
- data/lib/rails_pulse/route_indexes.rb +66 -0
- data/lib/rails_pulse/subscribers/exception_subscriber.rb +36 -0
- data/lib/rails_pulse/tracker.rb +29 -10
- data/lib/rails_pulse/version.rb +1 -1
- data/lib/rails_pulse.rb +1 -0
- data/lib/rails_pulse_server.ru +16 -47
- data/lib/tasks/rails_pulse.rake +83 -4
- data/lib/tasks/rails_pulse_assets.rake +15 -0
- data/lib/tasks/rails_pulse_tasks.rake +29 -0
- data/public/rails-pulse-assets/rails-pulse-icons.js +6 -2
- data/public/rails-pulse-assets/rails-pulse.css +1 -1
- data/public/rails-pulse-assets/rails-pulse.js +18 -117
- metadata +78 -37
- data/Rakefile +0 -554
- data/app/javascript/rails_pulse/controllers/form_controller.js +0 -39
- data/app/javascript/rails_pulse/controllers/period_selector_controller.js +0 -19
- data/app/javascript/rails_pulse/controllers/timezone_controller.js +0 -89
- data/app/javascript/rails_pulse/theme.js +0 -416
- data/config/brakeman.ignore +0 -190
- data/config/brakeman.yml +0 -68
- data/config/initializers/rails_pulse.rb +0 -287
- data/public/rails-pulse-assets/csp-test.js +0 -120
- data/public/rails-pulse-assets/rails-pulse-icons.js.map +0 -13
- data/public/rails-pulse-assets/rails-pulse.css.map +0 -1
- data/public/rails-pulse-assets/rails-pulse.js.map +0 -7
- data/vendor/assets/javascripts/rails-pulse-icons.js +0 -93
- data/vendor/assets/javascripts/rails-pulse-icons.js.map +0 -13
- data/vendor/assets/javascripts/rails-pulse.js +0 -157
- data/vendor/assets/javascripts/rails-pulse.js.map +0 -7
- data/vendor/assets/stylesheets/rails-pulse.css +0 -1
- data/vendor/assets/stylesheets/rails-pulse.css.map +0 -1
|
@@ -56,28 +56,12 @@
|
|
|
56
56
|
flex-direction: column;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
.sidebar-menu__group-label {
|
|
60
|
-
color: var(--color-text-subtle);
|
|
61
|
-
font-size: var(--text-xs);
|
|
62
|
-
font-weight: var(--font-medium);
|
|
63
|
-
padding: var(--size-1_5) var(--size-2);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
59
|
.sidebar-menu__items {
|
|
67
60
|
display: flex;
|
|
68
61
|
flex-direction: column;
|
|
69
62
|
row-gap: var(--size-1);
|
|
70
63
|
}
|
|
71
64
|
|
|
72
|
-
.sidebar-menu__sub {
|
|
73
|
-
border-inline-start-width: var(--border);
|
|
74
|
-
display: flex;
|
|
75
|
-
flex-direction: column;
|
|
76
|
-
margin-inline-start: var(--size-4);
|
|
77
|
-
padding: var(--size-0_5) var(--size-2);
|
|
78
|
-
row-gap: var(--size-1);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
65
|
/* Sheet component styles for mobile menu */
|
|
82
66
|
.sheet {
|
|
83
67
|
border: 0;
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/* Storage status page */
|
|
2
|
+
|
|
3
|
+
.health-badge-link {
|
|
4
|
+
text-decoration: none;
|
|
5
|
+
color: inherit;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.health-badge-link:hover {
|
|
9
|
+
text-decoration: none;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.health-badge-link:hover .badge {
|
|
13
|
+
background-color: var(--color-secondary);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.storage-overview-card {
|
|
17
|
+
min-height: 6.5rem;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.storage-panel-stats {
|
|
21
|
+
display: grid;
|
|
22
|
+
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
23
|
+
gap: var(--size-4);
|
|
24
|
+
margin-bottom: var(--size-4);
|
|
25
|
+
padding-bottom: var(--size-4);
|
|
26
|
+
border-bottom: 1px solid var(--color-border);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.storage-panel-tables {
|
|
30
|
+
display: flex;
|
|
31
|
+
flex-direction: column;
|
|
32
|
+
gap: var(--size-3);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.storage-panel-row {
|
|
36
|
+
display: grid;
|
|
37
|
+
grid-template-columns: 7.5rem 1fr auto;
|
|
38
|
+
gap: var(--size-3);
|
|
39
|
+
align-items: center;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.storage-panel-row__label {
|
|
43
|
+
white-space: nowrap;
|
|
44
|
+
overflow: hidden;
|
|
45
|
+
text-overflow: ellipsis;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.storage-panel-row__count {
|
|
49
|
+
white-space: nowrap;
|
|
50
|
+
font-variant-numeric: tabular-nums;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.storage-fill--compact {
|
|
54
|
+
margin-block: 0;
|
|
55
|
+
height: 0.375rem;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@media (max-width: 768px) {
|
|
59
|
+
.storage-panel-stats {
|
|
60
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.storage-panel-row {
|
|
64
|
+
grid-template-columns: 1fr auto;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.storage-panel-row .storage-fill {
|
|
68
|
+
grid-column: 1 / -1;
|
|
69
|
+
order: 3;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.storage-health--healthy {
|
|
74
|
+
color: var(--color-positive);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.storage-health--warning {
|
|
78
|
+
color: var(--color-warning-text);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.storage-health--critical,
|
|
82
|
+
.storage-health--disabled,
|
|
83
|
+
.storage-health--error {
|
|
84
|
+
color: var(--color-negative);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.storage-tables {
|
|
88
|
+
display: flex;
|
|
89
|
+
flex-direction: column;
|
|
90
|
+
gap: var(--size-5);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.storage-table {
|
|
94
|
+
padding-bottom: var(--size-5);
|
|
95
|
+
border-bottom: 1px solid var(--color-border);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.storage-table:last-child {
|
|
99
|
+
padding-bottom: 0;
|
|
100
|
+
border-bottom: none;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.storage-table__header {
|
|
104
|
+
display: flex;
|
|
105
|
+
align-items: flex-start;
|
|
106
|
+
justify-content: space-between;
|
|
107
|
+
gap: var(--size-4);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.storage-table__counts {
|
|
111
|
+
display: flex;
|
|
112
|
+
align-items: center;
|
|
113
|
+
gap: var(--size-2);
|
|
114
|
+
flex-shrink: 0;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.storage-table__percent {
|
|
118
|
+
font-variant-numeric: tabular-nums;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.storage-table__percent--healthy {
|
|
122
|
+
color: var(--color-positive);
|
|
123
|
+
border-color: color-mix(in srgb, var(--color-positive) 30%, transparent);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.storage-table__percent--warning {
|
|
127
|
+
color: var(--color-warning-text);
|
|
128
|
+
border-color: color-mix(in srgb, #eab308 40%, transparent);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.storage-table__percent--critical,
|
|
132
|
+
.storage-table__percent--error {
|
|
133
|
+
color: var(--color-negative);
|
|
134
|
+
border-color: color-mix(in srgb, var(--color-negative) 30%, transparent);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.storage-table__percent--uncapped {
|
|
138
|
+
color: var(--color-text-subtle);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.storage-fill {
|
|
142
|
+
height: 0.5rem;
|
|
143
|
+
margin-block: var(--size-2);
|
|
144
|
+
border-radius: var(--rounded-full, 999px);
|
|
145
|
+
background: var(--color-secondary);
|
|
146
|
+
overflow: hidden;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.storage-fill__bar {
|
|
150
|
+
height: 100%;
|
|
151
|
+
border-radius: inherit;
|
|
152
|
+
min-width: 0;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.storage-fill__bar--healthy {
|
|
156
|
+
background: var(--color-positive);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.storage-fill__bar--warning {
|
|
160
|
+
background: #eab308;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.storage-fill__bar--critical,
|
|
164
|
+
.storage-fill__bar--error {
|
|
165
|
+
background: var(--color-negative);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.storage-fill__bar--uncapped {
|
|
169
|
+
background: transparent;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.storage-table__meta {
|
|
173
|
+
display: flex;
|
|
174
|
+
flex-wrap: wrap;
|
|
175
|
+
gap: var(--size-1) var(--size-4);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.storage-table__meta span + span::before {
|
|
179
|
+
content: none;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
@media (max-width: 768px) {
|
|
183
|
+
.storage-table__header {
|
|
184
|
+
flex-direction: column;
|
|
185
|
+
gap: var(--size-2);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
@@ -31,20 +31,6 @@
|
|
|
31
31
|
padding-right: 3px !important;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
/* Individual Tag */
|
|
35
|
-
.tag {
|
|
36
|
-
display: inline-flex;
|
|
37
|
-
align-items: center;
|
|
38
|
-
gap: 0.25rem;
|
|
39
|
-
padding: 0.25rem 0.5rem;
|
|
40
|
-
background-color: var(--color-background-secondary);
|
|
41
|
-
border: 1px solid var(--color-border);
|
|
42
|
-
border-radius: 9999px;
|
|
43
|
-
font-size: 0.875rem;
|
|
44
|
-
line-height: 1.25;
|
|
45
|
-
white-space: nowrap;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
34
|
/* Tag chips — pill shape with subtle translucent fill */
|
|
49
35
|
.tag-list .badge {
|
|
50
36
|
border-radius: 9999px;
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
.time-range-trigger:hover {
|
|
20
20
|
background: var(--color-secondary);
|
|
21
|
-
border-color: var(--color-border-
|
|
21
|
+
border-color: var(--color-border-dark);
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
.time-range-trigger svg {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
|
|
29
29
|
.time-range-trigger:has(+ [popover]:popover-open) {
|
|
30
30
|
background: var(--color-secondary);
|
|
31
|
-
border-color: var(--color-border-
|
|
31
|
+
border-color: var(--color-border-dark);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
.time-range-trigger:has(+ [popover]:popover-open) svg {
|
|
@@ -36,16 +36,16 @@
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
/* Light mode improvements */
|
|
39
|
-
html[data-color-scheme="
|
|
39
|
+
html:not([data-color-scheme="dark"]) .time-range-trigger {
|
|
40
40
|
border-color: rgba(0, 0, 0, 0.2);
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
html[data-color-scheme="
|
|
43
|
+
html:not([data-color-scheme="dark"]) .time-range-trigger:hover {
|
|
44
44
|
background: rgba(0, 0, 0, 0.05);
|
|
45
45
|
border-color: rgba(0, 0, 0, 0.3);
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
html[data-color-scheme="
|
|
48
|
+
html:not([data-color-scheme="dark"]) .time-range-trigger:has(+ [popover]:popover-open) {
|
|
49
49
|
background: rgba(0, 0, 0, 0.05);
|
|
50
50
|
border-color: rgba(0, 0, 0, 0.3);
|
|
51
51
|
}
|
|
@@ -3,37 +3,20 @@
|
|
|
3
3
|
.w-4 { width: 1rem; }
|
|
4
4
|
.w-6 { width: 1.5rem; }
|
|
5
5
|
.w-8 { width: 2rem; }
|
|
6
|
-
.w-12 { width: 3rem; }
|
|
7
6
|
.w-16 { width: 4rem; }
|
|
8
7
|
.w-20 { width: 5rem; }
|
|
9
8
|
.w-24 { width: 6rem; }
|
|
10
9
|
.w-28 { width: 7rem; }
|
|
11
10
|
.w-32 { width: 8rem; }
|
|
12
11
|
.w-36 { width: 9rem; }
|
|
13
|
-
.w-40 { width: 10rem; }
|
|
14
|
-
.w-44 { width: 11rem; }
|
|
15
12
|
.w-48 { width: 12rem; }
|
|
16
|
-
.w-52 { width: 13rem; }
|
|
17
13
|
.w-56 { width: 14rem; }
|
|
18
14
|
.w-60 { width: 15rem; }
|
|
19
|
-
.w-64 { width: 16rem; }
|
|
20
15
|
|
|
21
16
|
/* Min-width utilities */
|
|
22
|
-
.min-w-0 { min-width: 0; }
|
|
23
|
-
.min-w-4 { min-width: 1rem; }
|
|
24
|
-
.min-w-8 { min-width: 2rem; }
|
|
25
|
-
.min-w-12 { min-width: 3rem; }
|
|
26
|
-
.min-w-16 { min-width: 4rem; }
|
|
27
|
-
.min-w-20 { min-width: 5rem; }
|
|
28
|
-
.min-w-24 { min-width: 6rem; }
|
|
29
|
-
.min-w-32 { min-width: 8rem; }
|
|
30
17
|
|
|
31
18
|
/* Max-width utilities */
|
|
32
|
-
.max-w-xs { max-width: 20rem; }
|
|
33
|
-
.max-w-sm { max-width: 24rem; }
|
|
34
|
-
.max-w-md { max-width: 28rem; }
|
|
35
19
|
.max-w-lg { max-width: 32rem; }
|
|
36
|
-
.max-w-xl { max-width: 36rem; }
|
|
37
20
|
|
|
38
21
|
/* Table layout utilities */
|
|
39
22
|
.table-fixed { table-layout: fixed; }
|
|
@@ -53,7 +53,7 @@ module ChartTableConcern
|
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
def setup_chart_and_table_data
|
|
56
|
-
ransack_params =
|
|
56
|
+
ransack_params = self.ransack_params
|
|
57
57
|
|
|
58
58
|
unless partial_request?
|
|
59
59
|
# Setup chart data first using original time range (no sorting from table)
|
|
@@ -172,11 +172,18 @@ module ChartTableConcern
|
|
|
172
172
|
action_name == "show"
|
|
173
173
|
end
|
|
174
174
|
|
|
175
|
+
# Hook: override to return param keys that should be excluded from chart queries.
|
|
176
|
+
# Use this when a filter requires a JOIN that chart queries don't perform
|
|
177
|
+
# (e.g. route path/action filters that need rails_pulse_routes joined).
|
|
178
|
+
def chart_filter_exclusions
|
|
179
|
+
[]
|
|
180
|
+
end
|
|
181
|
+
|
|
175
182
|
# Builds ransack parameters for chart queries
|
|
176
183
|
# Common pattern: time range + optional duration filter + resource scope
|
|
177
184
|
# Handles "recent" mode where @page_timings.start_time/@end_time may be nil
|
|
178
185
|
def build_chart_ransack_params(ransack_params)
|
|
179
|
-
base_params = ransack_params.except(:s)
|
|
186
|
+
base_params = ransack_params.except(:s, *chart_filter_exclusions)
|
|
180
187
|
|
|
181
188
|
# Add time filters if we have time boundaries (not in "recent" mode)
|
|
182
189
|
if @page_timings&.start_time && @page_timings&.end_time
|
|
@@ -196,7 +203,7 @@ module ChartTableConcern
|
|
|
196
203
|
|
|
197
204
|
# Scope to specific resource on show pages
|
|
198
205
|
if show_action?
|
|
199
|
-
base_params.merge(
|
|
206
|
+
base_params.merge(resource_id_scope)
|
|
200
207
|
else
|
|
201
208
|
base_params
|
|
202
209
|
end
|
|
@@ -308,4 +315,9 @@ module ChartTableConcern
|
|
|
308
315
|
def current_resource
|
|
309
316
|
nil
|
|
310
317
|
end
|
|
318
|
+
|
|
319
|
+
# Ransack scope for chart summaries on show pages. Override to customize (e.g. IN queries).
|
|
320
|
+
def resource_id_scope
|
|
321
|
+
{ summarizable_id_eq: current_resource.id }
|
|
322
|
+
end
|
|
311
323
|
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# RansackParamsConcern
|
|
2
|
+
#
|
|
3
|
+
# Every filterable page reads its Ransack conditions from params[:q]. The
|
|
4
|
+
# dashboard's own forms always send a hash, but a hand-edited URL can make it
|
|
5
|
+
# a string or an array (`?q=foo`), and each caller would then invoke Hash
|
|
6
|
+
# methods on it and 500. Normalise once, here.
|
|
7
|
+
module RansackParamsConcern
|
|
8
|
+
extend ActiveSupport::Concern
|
|
9
|
+
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
# params[:q] as an ActionController::Parameters hash, or an empty one when
|
|
13
|
+
# the value is absent or not hash-shaped.
|
|
14
|
+
def ransack_params
|
|
15
|
+
q = params[:q]
|
|
16
|
+
q.respond_to?(:permit) ? q : ActionController::Parameters.new({})
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
# Supports both page-specific duration filters and global performance threshold filters.
|
|
6
6
|
module ResponseRangeConcern
|
|
7
7
|
extend ActiveSupport::Concern
|
|
8
|
+
include RansackParamsConcern
|
|
8
9
|
|
|
9
10
|
def setup_duration_range(type = :route)
|
|
10
|
-
ransack_params = params[:q] || {}
|
|
11
11
|
thresholds = RailsPulse.configuration.public_send("#{type}_thresholds")
|
|
12
12
|
|
|
13
13
|
# Check all duration-related parameters
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
# Normalizes times to beginning/end of hour or day based on range duration.
|
|
12
12
|
module TimeRangeConcern
|
|
13
13
|
extend ActiveSupport::Concern
|
|
14
|
+
include RansackParamsConcern
|
|
14
15
|
|
|
15
16
|
included do
|
|
16
17
|
# Define the constant in the including class - ordered by most common usage
|
|
@@ -40,8 +41,6 @@ module TimeRangeConcern
|
|
|
40
41
|
end_time = Time.zone.now
|
|
41
42
|
selected_time_range = default_key
|
|
42
43
|
|
|
43
|
-
ransack_params = params[:q] || {}
|
|
44
|
-
|
|
45
44
|
# Priority 1: Page-specific preset from dropdown (check this first!)
|
|
46
45
|
if ransack_params[:period_start_range].present? && ransack_params[:period_start_range].to_sym != :custom
|
|
47
46
|
# Predefined time range from dropdown
|
|
@@ -59,22 +58,30 @@ module TimeRangeConcern
|
|
|
59
58
|
elsif ransack_params[:period_start_range].present? && ransack_params[:period_start_range].to_sym == :custom && ransack_params[:custom_date_range].present? && ransack_params[:custom_date_range].include?(" to ")
|
|
60
59
|
# Custom datetime range from custom range picker
|
|
61
60
|
dates = ransack_params[:custom_date_range].split(" to ")
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
custom_start = parse_time_param(dates[0].strip)
|
|
62
|
+
custom_end = parse_time_param(dates[1].strip)
|
|
63
|
+
if custom_start && custom_end
|
|
64
|
+
start_time = custom_start
|
|
65
|
+
end_time = custom_end
|
|
66
|
+
selected_time_range = :custom
|
|
67
|
+
end
|
|
65
68
|
# Priority 3: Page-specific filters (chart zoom)
|
|
66
69
|
elsif ransack_params[:occurred_at_gteq].present? && ransack_params[:occurred_at_lt].present?
|
|
67
70
|
# Custom time range from chart zoom
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
+
zoom_start = parse_time_param(ransack_params[:occurred_at_gteq])
|
|
72
|
+
zoom_end = parse_time_param(ransack_params[:occurred_at_lt])
|
|
73
|
+
if zoom_start && zoom_end
|
|
74
|
+
start_time = zoom_start
|
|
75
|
+
end_time = zoom_end
|
|
76
|
+
selected_time_range = :custom
|
|
77
|
+
end
|
|
71
78
|
# Priority 4: Time range selector (from session)
|
|
72
79
|
elsif session[:time_range_preference].present?
|
|
73
80
|
preference = session[:time_range_preference]
|
|
74
81
|
if preference.is_a?(Hash) && preference["type"] == "custom"
|
|
75
82
|
# Custom range from time range selector
|
|
76
|
-
start_time = parse_time_param(preference["start_time"])
|
|
77
|
-
end_time = parse_time_param(preference["end_time"])
|
|
83
|
+
start_time = parse_time_param(preference["start_time"]) || start_time
|
|
84
|
+
end_time = parse_time_param(preference["end_time"]) || end_time
|
|
78
85
|
selected_time_range = :custom
|
|
79
86
|
else
|
|
80
87
|
# Preset from time range selector
|
|
@@ -90,8 +97,8 @@ module TimeRangeConcern
|
|
|
90
97
|
end
|
|
91
98
|
# Priority 5: Global filters (from session)
|
|
92
99
|
elsif session_global_filters["start_time"].present? || session_global_filters["end_time"].present?
|
|
93
|
-
start_time = parse_time_param(session_global_filters["start_time"])
|
|
94
|
-
end_time = parse_time_param(session_global_filters["end_time"])
|
|
100
|
+
start_time = parse_time_param(session_global_filters["start_time"]) || start_time
|
|
101
|
+
end_time = parse_time_param(session_global_filters["end_time"]) || end_time
|
|
95
102
|
selected_time_range = :custom
|
|
96
103
|
end
|
|
97
104
|
# Priority 6: Default time range (already set above)
|
|
@@ -112,17 +119,24 @@ module TimeRangeConcern
|
|
|
112
119
|
|
|
113
120
|
private
|
|
114
121
|
|
|
122
|
+
# Returns nil for anything that does not parse, so a hand-edited or stale
|
|
123
|
+
# value falls back to the default range instead of raising.
|
|
115
124
|
def parse_time_param(param)
|
|
116
125
|
case param
|
|
117
126
|
when Time, DateTime
|
|
118
127
|
param.in_time_zone
|
|
119
128
|
when String
|
|
129
|
+
return nil if param.blank?
|
|
130
|
+
|
|
120
131
|
# Parse as server local time (not UTC, not Time.zone)
|
|
121
132
|
# This ensures flatpickr datetime strings are interpreted in server's timezone
|
|
122
133
|
Time.parse(param).localtime
|
|
134
|
+
when Numeric
|
|
135
|
+
Time.zone.at(param)
|
|
123
136
|
else
|
|
124
|
-
|
|
125
|
-
Time.zone.at(param.to_i)
|
|
137
|
+
nil
|
|
126
138
|
end
|
|
139
|
+
rescue ArgumentError, TypeError, RangeError
|
|
140
|
+
nil
|
|
127
141
|
end
|
|
128
142
|
end
|