rails_pulse 0.1.4 → 0.2.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.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +78 -0
  3. data/Rakefile +152 -3
  4. data/app/assets/images/rails_pulse/rails-pulse-logo.png +0 -0
  5. data/app/assets/stylesheets/rails_pulse/components/datepicker.css +191 -0
  6. data/app/assets/stylesheets/rails_pulse/components/switch.css +36 -0
  7. data/app/assets/stylesheets/rails_pulse/components/tags.css +98 -0
  8. data/app/assets/stylesheets/rails_pulse/components/utilities.css +26 -0
  9. data/app/controllers/concerns/response_range_concern.rb +15 -2
  10. data/app/controllers/concerns/tag_filter_concern.rb +26 -0
  11. data/app/controllers/concerns/time_range_concern.rb +27 -8
  12. data/app/controllers/rails_pulse/application_controller.rb +73 -0
  13. data/app/controllers/rails_pulse/queries_controller.rb +4 -1
  14. data/app/controllers/rails_pulse/requests_controller.rb +40 -8
  15. data/app/controllers/rails_pulse/routes_controller.rb +4 -2
  16. data/app/controllers/rails_pulse/tags_controller.rb +51 -0
  17. data/app/helpers/rails_pulse/application_helper.rb +2 -0
  18. data/app/helpers/rails_pulse/form_helper.rb +75 -0
  19. data/app/helpers/rails_pulse/tags_helper.rb +29 -0
  20. data/app/javascript/rails_pulse/application.js +6 -0
  21. data/app/javascript/rails_pulse/controllers/custom_range_controller.js +115 -0
  22. data/app/javascript/rails_pulse/controllers/datepicker_controller.js +48 -0
  23. data/app/javascript/rails_pulse/controllers/global_filters_controller.js +110 -0
  24. data/app/models/concerns/taggable.rb +61 -0
  25. data/app/models/rails_pulse/queries/tables/index.rb +10 -2
  26. data/app/models/rails_pulse/query.rb +2 -0
  27. data/app/models/rails_pulse/request.rb +9 -1
  28. data/app/models/rails_pulse/route.rb +2 -0
  29. data/app/models/rails_pulse/routes/tables/index.rb +10 -2
  30. data/app/services/rails_pulse/summary_service.rb +2 -0
  31. data/app/views/layouts/rails_pulse/_global_filters.html.erb +84 -0
  32. data/app/views/layouts/rails_pulse/_menu_items.html.erb +5 -5
  33. data/app/views/layouts/rails_pulse/application.html.erb +8 -5
  34. data/app/views/rails_pulse/components/_page_header.html.erb +20 -0
  35. data/app/views/rails_pulse/operations/show.html.erb +1 -1
  36. data/app/views/rails_pulse/queries/_table.html.erb +3 -1
  37. data/app/views/rails_pulse/queries/index.html.erb +3 -7
  38. data/app/views/rails_pulse/queries/show.html.erb +3 -7
  39. data/app/views/rails_pulse/requests/_table.html.erb +3 -1
  40. data/app/views/rails_pulse/requests/index.html.erb +44 -62
  41. data/app/views/rails_pulse/requests/show.html.erb +1 -1
  42. data/app/views/rails_pulse/routes/_requests_table.html.erb +3 -1
  43. data/app/views/rails_pulse/routes/_table.html.erb +3 -1
  44. data/app/views/rails_pulse/routes/index.html.erb +4 -8
  45. data/app/views/rails_pulse/routes/show.html.erb +3 -7
  46. data/app/views/rails_pulse/tags/_tag_manager.html.erb +73 -0
  47. data/config/routes.rb +5 -0
  48. data/db/rails_pulse_schema.rb +3 -0
  49. data/lib/generators/rails_pulse/install_generator.rb +21 -2
  50. data/lib/generators/rails_pulse/templates/db/rails_pulse_schema.rb +3 -0
  51. data/lib/generators/rails_pulse/templates/rails_pulse.rb +21 -0
  52. data/lib/generators/rails_pulse/upgrade_generator.rb +145 -29
  53. data/lib/rails_pulse/configuration.rb +16 -1
  54. data/lib/rails_pulse/version.rb +1 -1
  55. data/public/rails-pulse-assets/rails-pulse-icons.js +16 -15
  56. data/public/rails-pulse-assets/rails-pulse-icons.js.map +1 -1
  57. data/public/rails-pulse-assets/rails-pulse.css +1 -1
  58. data/public/rails-pulse-assets/rails-pulse.css.map +1 -1
  59. data/public/rails-pulse-assets/rails-pulse.js +73 -69
  60. data/public/rails-pulse-assets/rails-pulse.js.map +4 -4
  61. metadata +17 -3
  62. data/app/views/rails_pulse/components/_breadcrumbs.html.erb +0 -12
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4d5eb9055acbfc951ef6874c484b10f0c4c9a02f4886148fd917ec1f83cdc386
4
- data.tar.gz: fe1db6e87b0788868ab99a4c5ee2c818fb2720b8aaa88a59d86023a8a4bc0f92
3
+ metadata.gz: a8ed469f307dd71e912b46a4da0a0daa60abbdc262a8dbd12604db00a66335fe
4
+ data.tar.gz: 8af5a6bf946a86e1bc277c1d719100ace739bb429c07969fd1e4ec31887d6f70
5
5
  SHA512:
6
- metadata.gz: 17141eaefe36c4ee6e857fe36ae3ee7c9e500cb6b3dc07ba79b2ba20248cb1a01920d5f0441c09279af0ab04efa020ffe144aeef1f4fb00036094378c1e2fc65
7
- data.tar.gz: 8bf37d00fc3b161eb84587eef5562cebe493adb6a0729897f00aed9d38a69b1847ba82781473a8d2b3deb4ad1acea764b014460a758bc12ef686028096974355
6
+ metadata.gz: eceb3af7e713b6049688e50a8fd79af7b2f09c7720158c8d15257235b0c7488ebe7c0eccfaab3cd5dfb5923f80e9a8bd8693d0a9fb2f4e021aca8846c4ac435c
7
+ data.tar.gz: f982a50256d99968eb68fee89e6380d3f75511e6c3e8e878ddca375919f6a68a201f9ce46c6868e2425c127ae1862ca0d1895b0e01afea89a9af6a4e85d7f0f9
data/README.md CHANGED
@@ -25,6 +25,10 @@
25
25
  - [Authentication](#authentication)
26
26
  - [Authentication Setup](#authentication-setup)
27
27
  - [Authentication Examples](#authentication-examples)
28
+ - [Tagging System](#tagging-system)
29
+ - [Configuring Tags](#configuring-tags)
30
+ - [Using Tags](#using-tags)
31
+ - [Filtering by Tags](#filtering-by-tags)
28
32
  - [Data Management](#data-management)
29
33
  - [Cleanup Strategies](#cleanup-strategies)
30
34
  - [Cleanup Configuration](#cleanup-configuration)
@@ -58,6 +62,11 @@ Rails Pulse is a comprehensive performance monitoring and debugging gem that pro
58
62
  - Smart caching with minimal performance overhead
59
63
  - Multiple database support (SQLite, PostgreSQL, MySQL)
60
64
 
65
+ ### Organization & Filtering
66
+ - Flexible tagging system for routes, requests, and queries
67
+ - Filter performance data by custom tags
68
+ - Organize monitoring data by environment, priority, or custom categories
69
+
61
70
  ## Screenshots
62
71
 
63
72
  <table>
@@ -176,6 +185,9 @@ RailsPulse.configure do |config|
176
185
  config.ignored_requests = [] # Array of request patterns to ignore
177
186
  config.ignored_queries = [] # Array of query patterns to ignore
178
187
 
188
+ # Tagging system - define available tags for categorizing performance data
189
+ config.tags = ["production", "staging", "critical", "needs-optimization"]
190
+
179
191
  # Data cleanup
180
192
  config.archiving_enabled = true # Enable automatic cleanup
181
193
  config.full_retention_period = 2.weeks # Delete records older than this
@@ -243,6 +255,72 @@ config.authentication_method = proc {
243
255
  }
244
256
  ```
245
257
 
258
+ ## Tagging System
259
+
260
+ Rails Pulse includes a flexible tagging system that allows you to categorize and organize your performance data. Tag routes, requests, and queries with custom labels to better organize and filter your monitoring data.
261
+
262
+ ### Configuring Tags
263
+
264
+ Define available tags in your Rails Pulse initializer:
265
+
266
+ ```ruby
267
+ RailsPulse.configure do |config|
268
+ config.tags = [
269
+ "production",
270
+ "staging",
271
+ "critical",
272
+ "needs-optimization",
273
+ "high-traffic",
274
+ "background-job"
275
+ ]
276
+ end
277
+ ```
278
+
279
+ ### Using Tags
280
+
281
+ **Tag from the UI:**
282
+
283
+ 1. Navigate to any route, request, or query detail page
284
+ 2. Click the "+ tag" button next to the record
285
+ 3. Select from your configured tags
286
+ 4. Remove tags by clicking the × button on any tag badge
287
+
288
+ **Tag Programmatically:**
289
+
290
+ ```ruby
291
+ # Tag a route
292
+ route = RailsPulse::Route.find_by(path: "/api/users")
293
+ route.add_tag("critical")
294
+ route.add_tag("high-traffic")
295
+
296
+ # Tag a query
297
+ query = RailsPulse::Query.find_by(normalized_sql: "SELECT * FROM users WHERE id = ?")
298
+ query.add_tag("needs-optimization")
299
+
300
+ # Remove a tag
301
+ route.remove_tag("critical")
302
+
303
+ # Check if has tag
304
+ route.has_tag?("production") # => true
305
+ ```
306
+
307
+ ### Filtering by Tags
308
+
309
+ Use the global filters modal to filter performance data by tags:
310
+
311
+ 1. Click the filter icon in the top navigation
312
+ 2. Select one or more tags from the tag selector
313
+ 3. Apply filters to see only records with those tags
314
+ 4. Tags appear as badges in all data tables for quick visual identification
315
+
316
+ **Common Tagging Strategies:**
317
+
318
+ - **By Environment**: `production`, `staging`, `development`
319
+ - **By Priority**: `critical`, `high`, `medium`, `low`
320
+ - **By Status**: `needs-optimization`, `investigating`, `resolved`
321
+ - **By Type**: `api`, `background-job`, `user-facing`, `admin`
322
+ - **By Team**: `team-frontend`, `team-backend`, `team-data`
323
+
246
324
  ## Data Management
247
325
 
248
326
  Rails Pulse provides data cleanup to prevent your monitoring database from growing indefinitely while preserving essential performance insights.
data/Rakefile CHANGED
@@ -85,7 +85,7 @@ task :test do
85
85
  puts "=" * 50
86
86
  puts
87
87
 
88
- sh "rails test test/controllers test/helpers test/instrumentation test/jobs test/models test/services test/system"
88
+ sh "rails test test/controllers test/helpers test/instrumentation test/jobs test/models test/services"
89
89
  end
90
90
 
91
91
  desc "Setup database for specific Rails version and database"
@@ -137,10 +137,16 @@ task :test_matrix do
137
137
  total_combinations = databases.size * rails_versions.size
138
138
  current = 0
139
139
 
140
+ # Check if system tests should be included
141
+ include_system_tests = ENV['BROWSER'] == 'true'
142
+ test_paths = "test/controllers test/helpers test/instrumentation test/jobs test/models test/services"
143
+ test_paths += " test/system" if include_system_tests
144
+
140
145
  puts "\n" + "=" * 60
141
146
  puts "🚀 Rails Pulse Full Test Matrix"
142
147
  puts "=" * 60
143
148
  puts "Testing #{total_combinations} combinations..."
149
+ puts "System tests: #{include_system_tests ? 'ENABLED (BROWSER=true)' : 'DISABLED (headless mode)'}"
144
150
  puts "=" * 60
145
151
 
146
152
  databases.each do |database|
@@ -158,10 +164,10 @@ task :test_matrix do
158
164
  # Then run the tests
159
165
  if rails_version == "rails-8-0" && database == "sqlite3"
160
166
  # Current default setup
161
- sh "bundle exec rake test"
167
+ sh "BROWSER=#{ENV['BROWSER']} rails test #{test_paths}"
162
168
  else
163
169
  # Use appraisal with specific database
164
- sh "DB=#{database} bundle exec appraisal #{rails_version} rake test"
170
+ sh "DB=#{database} BROWSER=#{ENV['BROWSER']} bundle exec appraisal #{rails_version} rails test #{test_paths}"
165
171
  end
166
172
 
167
173
  puts "✅ PASSED: #{database} + #{rails_version}"
@@ -188,5 +194,148 @@ task :test_matrix do
188
194
  end
189
195
  end
190
196
 
197
+ desc "Pre-release testing with comprehensive checks"
198
+ task :test_release do
199
+ puts "\n" + "=" * 70
200
+ puts "🚀 Rails Pulse Pre-Release Validation"
201
+ puts "=" * 70
202
+ puts
203
+
204
+ failed_tasks = []
205
+ current_step = 0
206
+ total_steps = 7
207
+
208
+ # Step 1: Git status check
209
+ current_step += 1
210
+ begin
211
+ puts "\n[#{current_step}/#{total_steps}] Checking git status..."
212
+ puts "-" * 70
213
+
214
+ git_status = `git status --porcelain`.strip
215
+ if !git_status.empty?
216
+ puts "❌ Git working directory is not clean!"
217
+ puts "\nUncommitted changes:"
218
+ puts git_status
219
+ puts "\nPlease commit or stash your changes before running pre-release tests."
220
+ failed_tasks << "git_status_check"
221
+ else
222
+ puts "✅ Git working directory is clean"
223
+ end
224
+ rescue => e
225
+ puts "⚠️ Warning: Could not check git status (#{e.message})"
226
+ end
227
+
228
+ # Step 2: RuboCop linting
229
+ current_step += 1
230
+ begin
231
+ puts "\n[#{current_step}/#{total_steps}] Running RuboCop linting..."
232
+ puts "-" * 70
233
+ sh "bundle exec rubocop"
234
+ puts "✅ Code style checks passed!"
235
+ rescue => e
236
+ puts "❌ RuboCop linting failed!"
237
+ puts " Error: #{e.message}"
238
+ failed_tasks << "rubocop"
239
+ end
240
+
241
+ # Step 3: Install Node dependencies
242
+ current_step += 1
243
+ begin
244
+ puts "\n[#{current_step}/#{total_steps}] Installing Node dependencies..."
245
+ puts "-" * 70
246
+ sh "npm install"
247
+ puts "✅ Node dependencies installed!"
248
+ rescue => e
249
+ puts "❌ npm install failed!"
250
+ puts " Error: #{e.message}"
251
+ failed_tasks << "npm_install"
252
+ end
253
+
254
+ # Step 4: Build and verify assets
255
+ current_step += 1
256
+ begin
257
+ puts "\n[#{current_step}/#{total_steps}] Building production assets..."
258
+ puts "-" * 70
259
+ sh "npm run build"
260
+
261
+ # Verify assets were built
262
+ assets_dir = "public/rails-pulse-assets"
263
+ if Dir.exist?(assets_dir) && !Dir.empty?(assets_dir)
264
+ puts "✅ Assets built successfully!"
265
+ puts " Location: #{assets_dir}"
266
+ else
267
+ puts "❌ Assets directory is missing or empty!"
268
+ failed_tasks << "asset_build_verification"
269
+ end
270
+ rescue => e
271
+ puts "❌ Asset building failed!"
272
+ puts " Error: #{e.message}"
273
+ failed_tasks << "npm_build"
274
+ end
275
+
276
+ # Step 5: Verify gem builds
277
+ current_step += 1
278
+ begin
279
+ puts "\n[#{current_step}/#{total_steps}] Verifying gem builds correctly..."
280
+ puts "-" * 70
281
+ sh "gem build rails_pulse.gemspec"
282
+
283
+ # Clean up the built gem
284
+ built_gems = Dir.glob("rails_pulse-*.gem")
285
+ built_gems.each { |gem_file| File.delete(gem_file) }
286
+
287
+ puts "✅ Gem builds successfully!"
288
+ rescue => e
289
+ puts "❌ Gem build failed!"
290
+ puts " Error: #{e.message}"
291
+ failed_tasks << "gem_build"
292
+ end
293
+
294
+ # Step 6: Run generator tests
295
+ current_step += 1
296
+ begin
297
+ puts "\n[#{current_step}/#{total_steps}] Running generator tests..."
298
+ puts "-" * 70
299
+ sh "./bin/test_generators"
300
+ puts "✅ Generator tests passed!"
301
+ rescue => e
302
+ puts "❌ Generator tests failed!"
303
+ puts " Error: #{e.message}"
304
+ failed_tasks << "test_generators"
305
+ end
306
+
307
+ # Step 7: Run full test matrix with system tests
308
+ current_step += 1
309
+ begin
310
+ puts "\n[#{current_step}/#{total_steps}] Running full test matrix with system tests..."
311
+ puts "-" * 70
312
+ sh "BROWSER=true rake test_matrix"
313
+ puts "✅ Test matrix passed!"
314
+ rescue => e
315
+ puts "❌ Test matrix failed!"
316
+ puts " Error: #{e.message}"
317
+ failed_tasks << "test_matrix"
318
+ end
319
+
320
+ # Print final results
321
+ puts "\n" + "=" * 70
322
+ puts "🏁 Pre-Release Validation Results"
323
+ puts "=" * 70
324
+
325
+ if failed_tasks.empty?
326
+ puts "🎉 All pre-release checks passed!"
327
+ puts "\n✅ Ready for release!"
328
+ puts "\nNext steps:"
329
+ puts " 1. Update version in lib/rails_pulse/version.rb"
330
+ puts " 2. Update Gemfile.lock files for all Rails versions"
331
+ puts " 3. Follow the release process in docs/releasing.md"
332
+ else
333
+ puts "❌ Failed checks (#{failed_tasks.size}/#{total_steps}):"
334
+ failed_tasks.each { |task| puts " • #{task}" }
335
+ puts "\n⚠️ Fix these issues before releasing."
336
+ exit 1
337
+ end
338
+ end
339
+
191
340
 
192
341
  task default: :test
@@ -0,0 +1,191 @@
1
+ @import url("https://esm.sh/flatpickr@4.6.13/dist/flatpickr.min.css");
2
+
3
+ .flatpickr-calendar {
4
+ --calendar-size: 250px;
5
+ --container-size: 220px;
6
+ --day-size: var(--size-8);
7
+
8
+ background: var(--color-bg);
9
+ border: 1px solid var(--color-border);
10
+ border-radius: var(--rounded-md);
11
+ box-shadow: var(--shadow-md);
12
+ font-size: var(--text-sm);
13
+ inline-size: var(--calendar-size);
14
+
15
+ .flatpickr-innerContainer {
16
+ justify-content: center;
17
+ padding-block-end: var(--size-3);
18
+ }
19
+
20
+ .flatpickr-days {
21
+ inline-size: var(--container-size);
22
+ }
23
+
24
+ .dayContainer {
25
+ inline-size: var(--container-size);
26
+ min-inline-size: var(--container-size);
27
+ max-inline-size: var(--container-size);
28
+ }
29
+
30
+ .dayContainer + .dayContainer {
31
+ box-shadow: -1px 0 0 var(--color-border);
32
+ }
33
+
34
+ .flatpickr-months {
35
+ .flatpickr-month {
36
+ color: var(--color-text);
37
+ }
38
+
39
+ span.cur-month {
40
+ font-size: var(--text-sm);
41
+ font-weight: var(--font-medium);
42
+ }
43
+
44
+ svg {
45
+ fill: var(--color-border-dark);
46
+ }
47
+
48
+ .flatpickr-prev-month:hover svg {
49
+ fill: var(--color-text);
50
+ }
51
+
52
+ .flatpickr-next-month:hover svg {
53
+ fill: var(--color-text);
54
+ }
55
+ }
56
+
57
+ .flatpickr-monthDropdown-months {
58
+ appearance: none;
59
+ border-radius: var(--rounded-md);
60
+ font-size: var(--text-sm);
61
+ font-weight: var(--font-medium);
62
+ line-height: var(--leading-normal);
63
+ padding: 0;
64
+ text-align: center;
65
+
66
+ &:hover {
67
+ background: var(--color-border-light);
68
+ }
69
+ }
70
+
71
+ .numInputWrapper {
72
+ input {
73
+ border-radius: var(--rounded-md);
74
+ color: var(--color-text);
75
+ font-size: var(--text-sm);
76
+ font-weight: var(--font-medium);
77
+ line-height: var(--leading-normal);
78
+ padding: 0;
79
+ text-align: center;
80
+ }
81
+
82
+ span {
83
+ border-color: var(--color-border);
84
+ }
85
+
86
+ span:hover {
87
+ background: transparent;
88
+ }
89
+
90
+ span.arrowUp::after {
91
+ border-bottom-color: var(--color-text);
92
+ }
93
+
94
+ span.arrowDown::after {
95
+ border-top-color: var(--color-text);
96
+ }
97
+
98
+ &:hover {
99
+ background: transparent;
100
+ }
101
+ }
102
+
103
+ .flatpickr-weekday {
104
+ color: var(--color-text-subtle);
105
+ font-weight: var(--font-normal);
106
+ }
107
+
108
+ .flatpickr-time {
109
+ .hasTime & {
110
+ border-top-color: var(--color-border);
111
+ }
112
+
113
+ .hasTime.noCalendar & {
114
+ border: 0;
115
+ }
116
+
117
+ .numInput {
118
+ background: transparent;
119
+ color: var(--color-text);
120
+ }
121
+
122
+ .flatpickr-time-separator {
123
+ color: var(--color-text);
124
+ }
125
+
126
+ .flatpickr-am-pm {
127
+ background: transparent;
128
+ color: var(--color-text);
129
+ }
130
+ }
131
+
132
+ .flatpickr-day {
133
+ border-radius: var(--rounded-md);
134
+ border-color: transparent !important;
135
+ box-shadow: none !important;
136
+ color: var(--color-text);
137
+ height: var(--day-size);
138
+ line-height: var(--day-size);
139
+ margin-block-start: var(--size-2);
140
+ max-width: var(--day-size);
141
+
142
+ &:is(.inRange) {
143
+ border-radius: 0;
144
+ }
145
+
146
+ &:is(.today, .inRange, :hover, :focus) {
147
+ background: var(--color-secondary);
148
+ color: var(--color-text);
149
+ }
150
+
151
+ &:is(
152
+ .flatpickr-disabled,
153
+ .flatpickr-disabled:hover,
154
+ .prevMonthDay,
155
+ .nextMonthDay,
156
+ .notAllowed,
157
+ .notAllowed.prevMonthDay,
158
+ .notAllowed.nextMonthDay
159
+ ) {
160
+ color: var(--color-text-subtle);
161
+ }
162
+
163
+ &:is(
164
+ .selected,
165
+ .startRange,
166
+ .endRange,
167
+ .selected.inRange,
168
+ .startRange.inRange,
169
+ .endRange.inRange,
170
+ .selected:focus,
171
+ .startRange:focus,
172
+ .endRange:focus,
173
+ .selected:hover,
174
+ .startRange:hover,
175
+ .endRange:hover,
176
+ .selected.prevMonthDay,
177
+ .startRange.prevMonthDay,
178
+ .endRange.prevMonthDay,
179
+ .selected.nextMonthDay,
180
+ .startRange.nextMonthDay,
181
+ .endRange.nextMonthDay
182
+ ) {
183
+ background: var(--color-primary);
184
+ color: var(--color-text-reversed);
185
+ }
186
+ }
187
+
188
+ &::before, &::after {
189
+ display: none;
190
+ }
191
+ }
@@ -0,0 +1,36 @@
1
+ .switch {
2
+ appearance: none;
3
+ background-color: var(--color-border);
4
+ border-color: transparent;
5
+ border-radius: var(--rounded-full);
6
+ border-width: var(--border-2);
7
+ block-size: var(--size-5);
8
+ inline-size: var(--size-9);
9
+ transition: background-color var(--time-150);
10
+
11
+ &:checked {
12
+ background-color: var(--color-primary);
13
+ }
14
+
15
+ &:checked::before {
16
+ margin-inline-start: var(--size-4);
17
+ }
18
+
19
+ &::before {
20
+ aspect-ratio: var(--aspect-square);
21
+ background-color: var(--color-text-reversed);
22
+ block-size: var(--size-full);
23
+ border-radius: var(--rounded-full);
24
+ content: "";
25
+ display: block;
26
+ transition: margin var(--time-150);
27
+ }
28
+
29
+ &:focus-visible {
30
+ outline: var(--border-2) solid var(--color-selected-dark);
31
+ }
32
+
33
+ &:disabled {
34
+ cursor: not-allowed; opacity: var(--opacity-50);
35
+ }
36
+ }
@@ -0,0 +1,98 @@
1
+ /* Tag Manager Container */
2
+ .breadcrumb-container {
3
+ display: flex;
4
+ align-items: center;
5
+ justify-content: space-between;
6
+ gap: 1rem;
7
+ flex-wrap: wrap;
8
+ }
9
+
10
+ .breadcrumb-tags {
11
+ display: flex;
12
+ align-items: center;
13
+ }
14
+
15
+ /* Tag Manager */
16
+ .tag-manager {
17
+ display: flex;
18
+ align-items: center;
19
+ gap: 0.5rem;
20
+ }
21
+
22
+ .tag-list {
23
+ display: flex;
24
+ align-items: center;
25
+ gap: 0.5rem;
26
+ flex-wrap: wrap;
27
+ }
28
+
29
+ /* Individual Tag */
30
+ .tag {
31
+ display: inline-flex;
32
+ align-items: center;
33
+ gap: 0.25rem;
34
+ padding: 0.25rem 0.5rem;
35
+ background-color: var(--color-background-secondary);
36
+ border: 1px solid var(--color-border);
37
+ border-radius: 0.375rem;
38
+ font-size: 0.875rem;
39
+ line-height: 1.25;
40
+ white-space: nowrap;
41
+ }
42
+
43
+ /* Tag Remove Button */
44
+ .tag-remove {
45
+ all: unset;
46
+ display: inline-flex;
47
+ align-items: center;
48
+ justify-content: center;
49
+ width: 1rem;
50
+ height: 1rem;
51
+ padding: 0;
52
+ margin: 0;
53
+ background: none;
54
+ border: none;
55
+ cursor: pointer;
56
+ color: currentColor;
57
+ opacity: 0.6;
58
+ transition: opacity 0.15s ease;
59
+ }
60
+
61
+ .tag-remove:hover {
62
+ opacity: 1;
63
+ }
64
+
65
+ .tag-remove span {
66
+ font-size: 1.25rem;
67
+ line-height: 1;
68
+ font-weight: bold;
69
+ }
70
+
71
+ /* Add Tag Container */
72
+ .tag-add-container {
73
+ position: relative;
74
+ display: inline-block;
75
+ }
76
+
77
+ .tag-add-button {
78
+ padding: 0.25rem 0.5rem;
79
+ font-size: 0.875rem;
80
+ line-height: 1.25;
81
+ white-space: nowrap;
82
+ }
83
+
84
+ /* Responsive Design */
85
+ @media (max-width: 768px) {
86
+ .breadcrumb-container {
87
+ flex-direction: column;
88
+ align-items: flex-start;
89
+ }
90
+
91
+ .breadcrumb-tags {
92
+ width: 100%;
93
+ }
94
+
95
+ .tag-manager {
96
+ width: 100%;
97
+ }
98
+ }
@@ -34,3 +34,29 @@
34
34
  .max-w-md { max-width: 28rem; }
35
35
  .max-w-lg { max-width: 32rem; }
36
36
  .max-w-xl { max-width: 36rem; }
37
+
38
+ /* Global filters active indicator */
39
+ .global-filters-active {
40
+ position: relative;
41
+ }
42
+
43
+ .global-filters-active::after {
44
+ content: "";
45
+ position: absolute;
46
+ top: -2px;
47
+ right: -2px;
48
+ width: 8px;
49
+ height: 8px;
50
+ background-color: var(--color-primary);
51
+ border-radius: 50%;
52
+ border: 2px solid var(--color-bg);
53
+ }
54
+
55
+ /* Flatpickr z-index fix - ensure calendar appears above dialogs */
56
+ .flatpickr-calendar,
57
+ .flatpickr-calendar.open,
58
+ .flatpickr-calendar.inline,
59
+ .flatpickr-calendar.static,
60
+ .flatpickr-calendar.static.open {
61
+ z-index: 999999 !important;
62
+ }
@@ -5,9 +5,11 @@ module ResponseRangeConcern
5
5
  ransack_params = params[:q] || {}
6
6
  thresholds = RailsPulse.configuration.public_send("#{type}_thresholds")
7
7
 
8
- # Check both avg_duration (for Summary) and duration (for Request/Operation)
9
- duration_param = ransack_params[:avg_duration] || ransack_params[:duration]
8
+ # Check all duration-related parameters
9
+ # avg_duration for Summary, duration for Request/Operation, duration_gteq for direct Ransack filtering
10
+ duration_param = ransack_params[:avg_duration] || ransack_params[:duration] || ransack_params[:duration_gteq]
10
11
 
12
+ # Priority 1: Page-specific duration filter
11
13
  if duration_param.present?
12
14
  selected_range = duration_param
13
15
  start_duration =
@@ -17,6 +19,17 @@ module ResponseRangeConcern
17
19
  when :critical then thresholds[:critical]
18
20
  else 0
19
21
  end
22
+ # Priority 2: Global performance threshold filter
23
+ elsif (global_threshold = session_global_filters["performance_threshold"]).present?
24
+ selected_range = global_threshold.to_sym
25
+ start_duration =
26
+ case global_threshold.to_sym
27
+ when :slow then thresholds[:slow]
28
+ when :very_slow then thresholds[:very_slow]
29
+ when :critical then thresholds[:critical]
30
+ else 0
31
+ end
32
+ # Priority 3: No filter (show all)
20
33
  else
21
34
  start_duration = 0
22
35
  selected_range = :all