rails_error_dashboard 0.1.0 → 0.1.3

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 (95) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +305 -703
  3. data/app/assets/stylesheets/rails_error_dashboard/_catppuccin_mocha.scss +107 -0
  4. data/app/assets/stylesheets/rails_error_dashboard/_components.scss +625 -0
  5. data/app/assets/stylesheets/rails_error_dashboard/_layout.scss +257 -0
  6. data/app/assets/stylesheets/rails_error_dashboard/_theme_variables.scss +203 -0
  7. data/app/assets/stylesheets/rails_error_dashboard/application.css +926 -15
  8. data/app/assets/stylesheets/rails_error_dashboard/application.css.map +7 -0
  9. data/app/assets/stylesheets/rails_error_dashboard/application.scss +61 -0
  10. data/app/controllers/rails_error_dashboard/application_controller.rb +18 -0
  11. data/app/controllers/rails_error_dashboard/errors_controller.rb +140 -4
  12. data/app/helpers/rails_error_dashboard/application_helper.rb +55 -0
  13. data/app/helpers/rails_error_dashboard/backtrace_helper.rb +91 -0
  14. data/app/helpers/rails_error_dashboard/overview_helper.rb +78 -0
  15. data/app/helpers/rails_error_dashboard/user_agent_helper.rb +118 -0
  16. data/app/jobs/rails_error_dashboard/application_job.rb +19 -0
  17. data/app/jobs/rails_error_dashboard/async_error_logging_job.rb +48 -0
  18. data/app/jobs/rails_error_dashboard/baseline_alert_job.rb +263 -0
  19. data/app/jobs/rails_error_dashboard/discord_error_notification_job.rb +4 -8
  20. data/app/jobs/rails_error_dashboard/email_error_notification_job.rb +2 -1
  21. data/app/jobs/rails_error_dashboard/pagerduty_error_notification_job.rb +5 -5
  22. data/app/jobs/rails_error_dashboard/slack_error_notification_job.rb +10 -6
  23. data/app/jobs/rails_error_dashboard/webhook_error_notification_job.rb +5 -6
  24. data/app/mailers/rails_error_dashboard/application_mailer.rb +1 -1
  25. data/app/mailers/rails_error_dashboard/error_notification_mailer.rb +1 -1
  26. data/app/models/rails_error_dashboard/cascade_pattern.rb +74 -0
  27. data/app/models/rails_error_dashboard/error_baseline.rb +100 -0
  28. data/app/models/rails_error_dashboard/error_comment.rb +27 -0
  29. data/app/models/rails_error_dashboard/error_log.rb +471 -3
  30. data/app/models/rails_error_dashboard/error_occurrence.rb +49 -0
  31. data/app/views/layouts/rails_error_dashboard.html.erb +816 -178
  32. data/app/views/layouts/rails_error_dashboard_old_backup.html.erb +383 -0
  33. data/app/views/rails_error_dashboard/error_notification_mailer/error_alert.html.erb +3 -10
  34. data/app/views/rails_error_dashboard/error_notification_mailer/error_alert.text.erb +1 -2
  35. data/app/views/rails_error_dashboard/errors/_error_row.html.erb +78 -0
  36. data/app/views/rails_error_dashboard/errors/_pattern_insights.html.erb +209 -0
  37. data/app/views/rails_error_dashboard/errors/_stats.html.erb +34 -0
  38. data/app/views/rails_error_dashboard/errors/_timeline.html.erb +167 -0
  39. data/app/views/rails_error_dashboard/errors/analytics.html.erb +152 -56
  40. data/app/views/rails_error_dashboard/errors/correlation.html.erb +373 -0
  41. data/app/views/rails_error_dashboard/errors/index.html.erb +294 -138
  42. data/app/views/rails_error_dashboard/errors/overview.html.erb +253 -0
  43. data/app/views/rails_error_dashboard/errors/platform_comparison.html.erb +399 -0
  44. data/app/views/rails_error_dashboard/errors/show.html.erb +781 -65
  45. data/config/routes.rb +9 -0
  46. data/db/migrate/20251225071314_add_optimized_indexes_to_error_logs.rb +66 -0
  47. data/db/migrate/20251225074653_remove_environment_from_error_logs.rb +26 -0
  48. data/db/migrate/20251225085859_add_enhanced_metrics_to_error_logs.rb +12 -0
  49. data/db/migrate/20251225093603_add_similarity_tracking_to_error_logs.rb +9 -0
  50. data/db/migrate/20251225100236_create_error_occurrences.rb +31 -0
  51. data/db/migrate/20251225101920_create_cascade_patterns.rb +33 -0
  52. data/db/migrate/20251225102500_create_error_baselines.rb +38 -0
  53. data/db/migrate/20251226020000_add_workflow_fields_to_error_logs.rb +27 -0
  54. data/db/migrate/20251226020100_create_error_comments.rb +18 -0
  55. data/lib/generators/rails_error_dashboard/install/install_generator.rb +276 -1
  56. data/lib/generators/rails_error_dashboard/install/templates/initializer.rb +272 -37
  57. data/lib/generators/rails_error_dashboard/solid_queue/solid_queue_generator.rb +36 -0
  58. data/lib/generators/rails_error_dashboard/solid_queue/templates/queue.yml +55 -0
  59. data/lib/rails_error_dashboard/commands/batch_delete_errors.rb +1 -1
  60. data/lib/rails_error_dashboard/commands/batch_resolve_errors.rb +2 -2
  61. data/lib/rails_error_dashboard/commands/log_error.rb +272 -7
  62. data/lib/rails_error_dashboard/commands/resolve_error.rb +16 -0
  63. data/lib/rails_error_dashboard/configuration.rb +90 -5
  64. data/lib/rails_error_dashboard/error_reporter.rb +15 -7
  65. data/lib/rails_error_dashboard/logger.rb +105 -0
  66. data/lib/rails_error_dashboard/middleware/error_catcher.rb +17 -10
  67. data/lib/rails_error_dashboard/plugin.rb +6 -3
  68. data/lib/rails_error_dashboard/plugin_registry.rb +2 -2
  69. data/lib/rails_error_dashboard/plugins/audit_log_plugin.rb +0 -1
  70. data/lib/rails_error_dashboard/plugins/jira_integration_plugin.rb +3 -4
  71. data/lib/rails_error_dashboard/plugins/metrics_plugin.rb +1 -3
  72. data/lib/rails_error_dashboard/queries/analytics_stats.rb +44 -6
  73. data/lib/rails_error_dashboard/queries/baseline_stats.rb +107 -0
  74. data/lib/rails_error_dashboard/queries/co_occurring_errors.rb +86 -0
  75. data/lib/rails_error_dashboard/queries/dashboard_stats.rb +242 -2
  76. data/lib/rails_error_dashboard/queries/error_cascades.rb +74 -0
  77. data/lib/rails_error_dashboard/queries/error_correlation.rb +375 -0
  78. data/lib/rails_error_dashboard/queries/errors_list.rb +106 -10
  79. data/lib/rails_error_dashboard/queries/filter_options.rb +0 -1
  80. data/lib/rails_error_dashboard/queries/platform_comparison.rb +254 -0
  81. data/lib/rails_error_dashboard/queries/similar_errors.rb +93 -0
  82. data/lib/rails_error_dashboard/services/backtrace_parser.rb +113 -0
  83. data/lib/rails_error_dashboard/services/baseline_alert_throttler.rb +88 -0
  84. data/lib/rails_error_dashboard/services/baseline_calculator.rb +269 -0
  85. data/lib/rails_error_dashboard/services/cascade_detector.rb +95 -0
  86. data/lib/rails_error_dashboard/services/pattern_detector.rb +268 -0
  87. data/lib/rails_error_dashboard/services/similarity_calculator.rb +144 -0
  88. data/lib/rails_error_dashboard/value_objects/error_context.rb +27 -1
  89. data/lib/rails_error_dashboard/version.rb +1 -1
  90. data/lib/rails_error_dashboard.rb +57 -7
  91. metadata +69 -10
  92. data/app/models/rails_error_dashboard/application_record.rb +0 -5
  93. data/lib/rails_error_dashboard/queries/developer_insights.rb +0 -277
  94. data/lib/rails_error_dashboard/queries/errors_list_v2.rb +0 -149
  95. data/lib/tasks/rails_error_dashboard_tasks.rake +0 -4
@@ -0,0 +1,375 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsErrorDashboard
4
+ module Queries
5
+ # Query object for error correlation analysis
6
+ #
7
+ # Provides analytics for correlating errors with:
8
+ # - Releases (app_version, git_sha)
9
+ # - Users (affected users, multi-error users)
10
+ # - Time patterns (hour-of-day correlation)
11
+ #
12
+ # @example
13
+ # correlation = ErrorCorrelation.new(days: 30)
14
+ # correlation.errors_by_version
15
+ # # => { "1.0.0" => { count: 100, error_types: 15, critical_count: 5 } }
16
+ class ErrorCorrelation
17
+ attr_reader :days
18
+
19
+ # @param days [Integer] Number of days to analyze (default: 30)
20
+ def initialize(days: 30)
21
+ @days = days
22
+ @start_date = days.days.ago
23
+ end
24
+
25
+ # Get error statistics grouped by app version
26
+ # @return [Hash] Version => { count, error_types, critical_count, platforms }
27
+ def errors_by_version
28
+ return {} unless has_version_column?
29
+
30
+ versions = base_query
31
+ .where.not(app_version: nil)
32
+ .group(:app_version)
33
+ .count
34
+
35
+ versions.each_with_object({}) do |(version, count), result|
36
+ errors = base_query.where(app_version: version)
37
+
38
+ # Count unique error types
39
+ error_types = errors.distinct.pluck(:error_type).count
40
+
41
+ # Count critical errors
42
+ critical_count = errors.select { |error| error.severity == :critical }.count
43
+
44
+ # Get platforms for this version
45
+ platforms = errors.distinct.pluck(:platform).compact
46
+
47
+ result[version] = {
48
+ count: count,
49
+ error_types: error_types,
50
+ critical_count: critical_count,
51
+ platforms: platforms,
52
+ first_seen: errors.minimum(:occurred_at),
53
+ last_seen: errors.maximum(:occurred_at)
54
+ }
55
+ end
56
+ end
57
+
58
+ # Get error statistics grouped by git SHA
59
+ # @return [Hash] SHA => { count, error_types, app_version }
60
+ def errors_by_git_sha
61
+ return {} unless has_git_sha_column?
62
+
63
+ shas = base_query
64
+ .where.not(git_sha: nil)
65
+ .group(:git_sha)
66
+ .count
67
+
68
+ shas.each_with_object({}) do |(sha, count), result|
69
+ errors = base_query.where(git_sha: sha)
70
+
71
+ # Get associated version (may be multiple)
72
+ versions = errors.distinct.pluck(:app_version).compact
73
+
74
+ result[sha] = {
75
+ count: count,
76
+ error_types: errors.distinct.pluck(:error_type).count,
77
+ app_versions: versions,
78
+ first_seen: errors.minimum(:occurred_at),
79
+ last_seen: errors.maximum(:occurred_at)
80
+ }
81
+ end
82
+ end
83
+
84
+ # Find problematic releases (versions with >2x average error rate)
85
+ # @return [Array<Hash>] Array of problematic version data
86
+ def problematic_releases
87
+ return [] unless has_version_column?
88
+
89
+ versions_data = errors_by_version
90
+ return [] if versions_data.empty?
91
+
92
+ total_errors = versions_data.values.map { |v| v[:count] }.sum
93
+ avg_errors = total_errors.to_f / versions_data.count
94
+ threshold = avg_errors * 2
95
+
96
+ versions_data
97
+ .select { |_, data| data[:count] > threshold }
98
+ .map do |version, data|
99
+ deviation = avg_errors > 0 ? ((data[:count] - avg_errors) / avg_errors * 100).round(1) : 0.0
100
+
101
+ {
102
+ version: version,
103
+ error_count: data[:count],
104
+ deviation_from_avg: deviation,
105
+ critical_count: data[:critical_count],
106
+ error_types: data[:error_types],
107
+ platforms: data[:platforms]
108
+ }
109
+ end
110
+ .sort_by { |v| -v[:error_count] }
111
+ end
112
+
113
+ # Find users affected by multiple different error types
114
+ # @param min_error_types [Integer] Minimum number of different error types (default: 2)
115
+ # @return [Array<Hash>] Users with multiple error type exposure
116
+ def multi_error_users(min_error_types: 2)
117
+ users_with_errors = base_query
118
+ .where.not(user_id: nil)
119
+ .group(:user_id, :error_type)
120
+ .count
121
+
122
+ # Group by user_id
123
+ users_by_id = users_with_errors.group_by { |(user_id, _), _| user_id }
124
+
125
+ users_by_id
126
+ .select { |_, error_data| error_data.count >= min_error_types }
127
+ .map do |user_id, error_data|
128
+ error_type_names = error_data.map { |(_, type), _| type }
129
+ total_errors = error_data.map { |_, count| count }.sum
130
+
131
+ {
132
+ user_id: user_id,
133
+ user_email: find_user_email(user_id),
134
+ error_types: error_type_names,
135
+ error_type_count: error_type_names.count,
136
+ total_errors: total_errors
137
+ }
138
+ end
139
+ .sort_by { |u| -u[:error_type_count] }
140
+ end
141
+
142
+ # Calculate user overlap between two error types
143
+ # Returns percentage of users affected by both errors
144
+ # @param error_type_a [String] First error type
145
+ # @param error_type_b [String] Second error type
146
+ # @return [Hash] Overlap statistics
147
+ def error_type_user_overlap(error_type_a, error_type_b)
148
+ users_a = base_query
149
+ .where(error_type: error_type_a)
150
+ .where.not(user_id: nil)
151
+ .distinct
152
+ .pluck(:user_id)
153
+
154
+ users_b = base_query
155
+ .where(error_type: error_type_b)
156
+ .where.not(user_id: nil)
157
+ .distinct
158
+ .pluck(:user_id)
159
+
160
+ overlap = users_a & users_b
161
+
162
+ {
163
+ error_type_a: error_type_a,
164
+ error_type_b: error_type_b,
165
+ users_a_count: users_a.count,
166
+ users_b_count: users_b.count,
167
+ overlap_count: overlap.count,
168
+ overlap_percentage: calculate_percentage(overlap.count, [ users_a.count, users_b.count ].min),
169
+ overlapping_user_ids: overlap.first(10) # Sample of overlapping users
170
+ }
171
+ end
172
+
173
+ # Analyze time-based correlation between error types
174
+ # Finds error types that tend to occur at similar hours of day
175
+ # @return [Hash] Error type pairs with correlation scores
176
+ def time_correlated_errors
177
+ # Get hourly distribution for each error type
178
+ error_types = base_query.distinct.pluck(:error_type)
179
+ return {} if error_types.count < 2
180
+
181
+ hourly_distributions = {}
182
+ error_types.each do |error_type|
183
+ distribution = base_query
184
+ .where(error_type: error_type)
185
+ .group_by { |error| error.occurred_at.hour }
186
+ .transform_values(&:count)
187
+
188
+ # Normalize to 0-23 hours
189
+ hourly_distributions[error_type] = (0..23).map { |h| distribution[h] || 0 }
190
+ end
191
+
192
+ # Calculate correlation between error type pairs
193
+ correlations = {}
194
+ error_types.combination(2).each do |type_a, type_b|
195
+ correlation = calculate_time_correlation(
196
+ hourly_distributions[type_a],
197
+ hourly_distributions[type_b]
198
+ )
199
+
200
+ # Only include significant correlations (>0.5)
201
+ if correlation > 0.5
202
+ correlations["#{type_a} <-> #{type_b}"] = {
203
+ error_type_a: type_a,
204
+ error_type_b: type_b,
205
+ correlation: correlation,
206
+ strength: classify_correlation_strength(correlation)
207
+ }
208
+ end
209
+ end
210
+
211
+ correlations.sort_by { |_, v| -v[:correlation] }.to_h
212
+ end
213
+
214
+ # Compare error rates across different time periods
215
+ # @return [Hash] Comparison of current vs previous period
216
+ def period_comparison
217
+ current_start = (@days / 2).days.ago
218
+ previous_start = @start_date
219
+ previous_end = current_start
220
+
221
+ current_errors = ErrorLog
222
+ .where("occurred_at >= ?", current_start)
223
+ .count
224
+
225
+ previous_errors = ErrorLog
226
+ .where("occurred_at >= ? AND occurred_at < ?", previous_start, previous_end)
227
+ .count
228
+
229
+ change_percentage = if previous_errors > 0
230
+ ((current_errors - previous_errors).to_f / previous_errors * 100).round(1)
231
+ else
232
+ current_errors > 0 ? 100.0 : 0.0
233
+ end
234
+
235
+ {
236
+ current_period: {
237
+ start: current_start,
238
+ end: Time.current,
239
+ count: current_errors
240
+ },
241
+ previous_period: {
242
+ start: previous_start,
243
+ end: previous_end,
244
+ count: previous_errors
245
+ },
246
+ change: current_errors - previous_errors,
247
+ change_percentage: change_percentage,
248
+ trend: determine_trend(change_percentage)
249
+ }
250
+ end
251
+
252
+ # Get top error types by platform
253
+ # Shows which errors are platform-specific vs cross-platform
254
+ # @return [Hash] Platform => top error types
255
+ def platform_specific_errors
256
+ platforms = base_query.distinct.pluck(:platform).compact
257
+
258
+ platforms.each_with_object({}) do |platform, result|
259
+ platform_errors = base_query.where(platform: platform)
260
+ top_errors = platform_errors
261
+ .group(:error_type)
262
+ .count
263
+ .sort_by { |_, count| -count }
264
+ .first(5)
265
+
266
+ result[platform] = top_errors.map do |error_type, count|
267
+ # Check if this error occurs on other platforms
268
+ other_platforms = base_query
269
+ .where(error_type: error_type)
270
+ .where.not(platform: platform)
271
+ .distinct
272
+ .pluck(:platform)
273
+ .compact
274
+
275
+ {
276
+ error_type: error_type,
277
+ count: count,
278
+ platform_specific: other_platforms.empty?,
279
+ also_on: other_platforms
280
+ }
281
+ end
282
+ end
283
+ end
284
+
285
+ private
286
+
287
+ def base_query
288
+ ErrorLog.where("occurred_at >= ?", @start_date)
289
+ end
290
+
291
+ # Check if app_version column exists
292
+ def has_version_column?
293
+ ErrorLog.column_names.include?("app_version")
294
+ end
295
+
296
+ # Check if git_sha column exists
297
+ def has_git_sha_column?
298
+ ErrorLog.column_names.include?("git_sha")
299
+ end
300
+
301
+ # Find user email
302
+ def find_user_email(user_id)
303
+ user_model = RailsErrorDashboard.configuration.user_model
304
+ user = user_model.constantize.find_by(id: user_id)
305
+ user&.email || "User ##{user_id}"
306
+ rescue StandardError
307
+ "User ##{user_id}"
308
+ end
309
+
310
+ # Calculate percentage
311
+ def calculate_percentage(part, whole)
312
+ return 0.0 if whole.zero?
313
+ (part.to_f / whole * 100).round(1)
314
+ end
315
+
316
+ # Calculate Pearson correlation coefficient between two time series
317
+ def calculate_time_correlation(series_a, series_b)
318
+ return 0.0 if series_a.sum.zero? || series_b.sum.zero?
319
+
320
+ n = series_a.length
321
+ return 0.0 if n.zero?
322
+
323
+ # Calculate means
324
+ mean_a = series_a.sum.to_f / n
325
+ mean_b = series_b.sum.to_f / n
326
+
327
+ # Calculate covariance and standard deviations
328
+ covariance = 0.0
329
+ std_a = 0.0
330
+ std_b = 0.0
331
+
332
+ n.times do |i|
333
+ diff_a = series_a[i] - mean_a
334
+ diff_b = series_b[i] - mean_b
335
+ covariance += diff_a * diff_b
336
+ std_a += diff_a**2
337
+ std_b += diff_b**2
338
+ end
339
+
340
+ # Avoid division by zero
341
+ denominator = Math.sqrt(std_a * std_b)
342
+ return 0.0 if denominator.zero?
343
+
344
+ (covariance / denominator).round(3)
345
+ end
346
+
347
+ # Classify correlation strength
348
+ def classify_correlation_strength(correlation)
349
+ abs_corr = correlation.abs
350
+ if abs_corr >= 0.8
351
+ :strong
352
+ elsif abs_corr >= 0.5
353
+ :moderate
354
+ else
355
+ :weak
356
+ end
357
+ end
358
+
359
+ # Determine trend based on change percentage
360
+ def determine_trend(change_percentage)
361
+ if change_percentage > 20
362
+ :increasing_significantly
363
+ elsif change_percentage > 5
364
+ :increasing
365
+ elsif change_percentage < -20
366
+ :decreasing_significantly
367
+ elsif change_percentage < -5
368
+ :decreasing
369
+ else
370
+ :stable
371
+ end
372
+ end
373
+ end
374
+ end
375
+ end
@@ -24,20 +24,19 @@ module RailsErrorDashboard
24
24
  private
25
25
 
26
26
  def apply_filters(query)
27
- query = filter_by_environment(query)
28
27
  query = filter_by_error_type(query)
29
28
  query = filter_by_resolved(query)
30
29
  query = filter_by_platform(query)
31
30
  query = filter_by_search(query)
31
+ query = filter_by_severity(query)
32
+ # Phase 3: Workflow filters
33
+ query = filter_by_status(query)
34
+ query = filter_by_assignment(query)
35
+ query = filter_by_priority(query)
36
+ query = filter_by_snoozed(query)
32
37
  query
33
38
  end
34
39
 
35
- def filter_by_environment(query)
36
- return query unless @filters[:environment].present?
37
-
38
- query.where(environment: @filters[:environment])
39
- end
40
-
41
40
  def filter_by_error_type(query)
42
41
  return query unless @filters[:error_type].present?
43
42
 
@@ -45,9 +44,25 @@ module RailsErrorDashboard
45
44
  end
46
45
 
47
46
  def filter_by_resolved(query)
48
- return query unless @filters[:unresolved] == "true" || @filters[:unresolved] == true
47
+ # Handle unresolved filter with explicit true/false values
48
+ # When checkbox is unchecked: unresolved=false → show all errors
49
+ # When checkbox is checked: unresolved=true → show only unresolved errors
50
+ # When no filter: nil → default to unresolved only
49
51
 
50
- query.unresolved
52
+ case @filters[:unresolved]
53
+ when false, "false", "0"
54
+ # Explicitly show all errors (resolved and unresolved)
55
+ query
56
+ when true, "true", "1"
57
+ # Explicitly show only unresolved errors
58
+ query.unresolved
59
+ when nil, ""
60
+ # Default: show only unresolved errors when no filter is set
61
+ query.unresolved
62
+ else
63
+ # Fallback: show only unresolved errors
64
+ query.unresolved
65
+ end
51
66
  end
52
67
 
53
68
  def filter_by_platform(query)
@@ -59,7 +74,88 @@ module RailsErrorDashboard
59
74
  def filter_by_search(query)
60
75
  return query unless @filters[:search].present?
61
76
 
62
- query.where("message ILIKE ?", "%#{@filters[:search]}%")
77
+ # Use PostgreSQL full-text search if available (much faster with GIN index)
78
+ # Otherwise fall back to LIKE query
79
+ if postgresql?
80
+ # Use to_tsquery for full-text search with GIN index
81
+ # This is dramatically faster on large datasets
82
+ search_term = @filters[:search].split.map { |word| "#{word}:*" }.join(" & ")
83
+ query.where("to_tsvector('english', message) @@ to_tsquery('english', ?)", search_term)
84
+ else
85
+ # Fall back to LIKE for SQLite/MySQL
86
+ # Use LOWER() for case-insensitive search
87
+ query.where("LOWER(message) LIKE LOWER(?)", "%#{@filters[:search]}%")
88
+ end
89
+ end
90
+
91
+ def postgresql?
92
+ ActiveRecord::Base.connection.adapter_name.downcase == "postgresql"
93
+ end
94
+
95
+ def filter_by_severity(query)
96
+ return query unless @filters[:severity].present?
97
+
98
+ # Map severity levels to error types
99
+ error_types = case @filters[:severity].to_sym
100
+ when :critical
101
+ ErrorLog::CRITICAL_ERROR_TYPES
102
+ when :high
103
+ ErrorLog::HIGH_SEVERITY_ERROR_TYPES
104
+ when :medium
105
+ ErrorLog::MEDIUM_SEVERITY_ERROR_TYPES
106
+ when :low
107
+ # Low severity = everything NOT in the other categories
108
+ all_categorized = ErrorLog::CRITICAL_ERROR_TYPES +
109
+ ErrorLog::HIGH_SEVERITY_ERROR_TYPES +
110
+ ErrorLog::MEDIUM_SEVERITY_ERROR_TYPES
111
+ # Use NOT IN to filter out categorized errors
112
+ return query.where.not(error_type: all_categorized)
113
+ else
114
+ return query
115
+ end
116
+
117
+ query.where(error_type: error_types)
118
+ end
119
+
120
+ # Phase 3: Workflow filter methods
121
+
122
+ def filter_by_status(query)
123
+ return query unless @filters[:status].present?
124
+ return query unless query.model.column_names.include?("status")
125
+
126
+ query.by_status(@filters[:status])
127
+ end
128
+
129
+ def filter_by_assignment(query)
130
+ return query unless @filters[:assigned_to].present?
131
+ return query unless query.model.column_names.include?("assigned_to")
132
+
133
+ case @filters[:assigned_to]
134
+ when "__unassigned__"
135
+ query.unassigned
136
+ when "__assigned__"
137
+ query.assigned
138
+ else
139
+ query.by_assignee(@filters[:assigned_to])
140
+ end
141
+ end
142
+
143
+ def filter_by_priority(query)
144
+ return query unless @filters[:priority_level].present?
145
+ return query unless query.model.column_names.include?("priority_level")
146
+
147
+ query.by_priority(@filters[:priority_level])
148
+ end
149
+
150
+ def filter_by_snoozed(query)
151
+ return query unless query.model.column_names.include?("snoozed_until")
152
+
153
+ # If hide_snoozed is checked, exclude snoozed errors
154
+ if @filters[:hide_snoozed] == "1" || @filters[:hide_snoozed] == true
155
+ query.active
156
+ else
157
+ query
158
+ end
63
159
  end
64
160
  end
65
161
  end
@@ -11,7 +11,6 @@ module RailsErrorDashboard
11
11
 
12
12
  def call
13
13
  {
14
- environments: ErrorLog.distinct.pluck(:environment).compact,
15
14
  error_types: ErrorLog.distinct.pluck(:error_type).compact.sort,
16
15
  platforms: ErrorLog.distinct.pluck(:platform).compact
17
16
  }