Module: RailsErrorDashboard::ApplicationHelper

Defined in:
app/helpers/rails_error_dashboard/application_helper.rb

Instance Method Summary collapse

Instance Method Details

Automatically converts URLs in text to clickable links that open in new window Also highlights inline code wrapped in backticks with syntax highlighting Also converts file paths to GitHub links if repository URL is configured Supports http://, https://, and common patterns like github.com/user/repo

Parameters:

  • text (String)

    The text containing URLs, file paths, and inline code

  • error (RailsErrorDashboard::ErrorLog, nil) (defaults to: nil)

    The error for context (to get repo URL)

Returns:

  • (String)

    HTML safe text with clickable links and styled code



206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# File 'app/helpers/rails_error_dashboard/application_helper.rb', line 206

def auto_link_urls(text, error: nil)
  return "" if text.blank?

  # Get repository URL from error's application or global config
  repo_url = if error&.application&.repository_url.present?
    error.application.repository_url
  elsif RailsErrorDashboard.configuration.git_repository_url.present?
    RailsErrorDashboard.configuration.git_repository_url
  end

  # First, protect inline code with backticks by replacing with placeholders
  code_blocks = []
  file_paths = []
  text_with_placeholders = text.gsub(/`([^`]+)`/) do |match|
    code_content = Regexp.last_match(1)

    # Check if the code block contains a file path pattern
    if repo_url && code_content =~ %r{^(app|lib|config|db|spec|test)/[^\s]+\.(rb|js|jsx|ts|tsx|erb|yml|yaml|json|css|scss)$}
      # It's a file path - save it and mark for GitHub linking
      file_paths << code_content
      "###FILE_PATH_#{file_paths.length - 1}###"
    else
      # Regular code block
      code_blocks << code_content
      "###CODE_BLOCK_#{code_blocks.length - 1}###"
    end
  end

  # Regex to match URLs (http://, https://, www., and common domains)
  url_regex = %r{
    (
      (?:https?://|www\.)           # http://, https://, or www.
      (?:[^\s<>"]+)                 # Domain and path (no spaces, <, >, or ")
      |
      (?:^|\s)                      # Start of string or whitespace
      (?:github\.com|gitlab\.com|bitbucket\.org|jira\.[^\s]+)
      /[^\s<>"]+                    # Path after domain
    )
  }xi

  # Replace URLs with clickable links
  linked_text = text_with_placeholders.gsub(url_regex) do |url|
    # Clean up the URL
    clean_url = url.strip

    # Add protocol if missing
    href = clean_url.start_with?("http://", "https://") ? clean_url : "https://#{clean_url}"

    # Truncate display text for very long URLs
    display_text = clean_url.length > 60 ? "#{clean_url[0..57]}..." : clean_url

    "<a href=\"#{ERB::Util.html_escape(href)}\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-primary text-decoration-underline\">#{ERB::Util.html_escape(display_text)}</a>"
  end

  # Restore file paths with GitHub links (elvish magic! 🧝‍♀️)
  linked_text.gsub!(/###FILE_PATH_(\d+)###/) do
    file_path = file_paths[Regexp.last_match(1).to_i]
    github_url = "#{repo_url.chomp('/')}/blob/main/#{file_path}"
    "<a href=\"#{ERB::Util.html_escape(github_url)}\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-decoration-none\" title=\"View on GitHub\">" \
    "<code class=\"inline-code-highlight file-path-link\">#{ERB::Util.html_escape(file_path)}</code></a>"
  end

  # Restore code blocks with styling
  linked_text.gsub!(/###CODE_BLOCK_(\d+)###/) do
    code_content = code_blocks[Regexp.last_match(1).to_i]
    "<code class=\"inline-code-highlight\">#{ERB::Util.html_escape(code_content)}</code>"
  end

  # Preserve line breaks and return as HTML safe
  simple_format(linked_text, {}, sanitize: false)
end

Generates a link to a git commit if repository URL is configured

Parameters:

  • git_sha (String)

    The git commit SHA

  • short (Boolean) (defaults to: true)

    Whether to show short SHA (7 chars) or full SHA

Returns:

  • (String)

    HTML safe link to commit or plain text if no repo configured



121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'app/helpers/rails_error_dashboard/application_helper.rb', line 121

def git_commit_link(git_sha, short: true)
  return "" if git_sha.blank?

  config = RailsErrorDashboard.configuration
  display_sha = short ? git_sha[0..6] : git_sha

  if config.git_repository_url.present?
    # Support GitHub, GitLab, Bitbucket URL formats
    commit_url = "#{config.git_repository_url.chomp("/")}/commit/#{git_sha}"
    link_to display_sha, commit_url, class: "text-decoration-none font-monospace", target: "_blank", rel: "noopener"
  else
    (:code, display_sha, class: "font-monospace")
  end
end

#local_time(time, format: :full, fallback: "N/A") ⇒ String

Renders a timestamp that will be automatically converted to user’s local timezone Server sends UTC timestamp, JavaScript converts to local timezone on page load

Parameters:

  • time (Time, DateTime, nil)

    The timestamp to display

  • format (Symbol) (defaults to: :full)

    Format preset (:full, :short, :date_only, :time_only, :datetime)

  • fallback (String) (defaults to: "N/A")

    Text to show if time is nil

Returns:

  • (String)

    HTML safe span with data attributes for JS conversion



142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'app/helpers/rails_error_dashboard/application_helper.rb', line 142

def local_time(time, format: :full, fallback: "N/A")
  return fallback if time.nil?

  # Convert to UTC if not already
  utc_time = time.respond_to?(:utc) ? time.utc : time

  # ISO 8601 format for JavaScript parsing
  iso_time = utc_time.iso8601

  # Format presets for data-format attribute
  format_string = case format
  when :full
    "%B %d, %Y %I:%M:%S %p"  # December 31, 2024 11:59:59 PM
  when :short
    "%m/%d %I:%M%p"          # 12/31 11:59PM
  when :date_only
    "%B %d, %Y"              # December 31, 2024
  when :time_only
    "%I:%M:%S %p"            # 11:59:59 PM
  when :datetime
    "%b %d, %Y %H:%M"        # Dec 31, 2024 23:59
  else
    format.to_s
  end

  (
    :span,
    utc_time.strftime(format_string + " UTC"),  # Fallback for non-JS browsers
    class: "local-time",
    data: {
      utc: iso_time,
      format: format_string
    }
  )
end

#local_time_ago(time, fallback: "N/A") ⇒ String

Renders a relative time (“3 hours ago”) that updates automatically

Parameters:

  • time (Time, DateTime, nil)

    The timestamp to display

  • fallback (String) (defaults to: "N/A")

    Text to show if time is nil

Returns:

  • (String)

    HTML safe span with data attributes for JS conversion



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'app/helpers/rails_error_dashboard/application_helper.rb', line 182

def local_time_ago(time, fallback: "N/A")
  return fallback if time.nil?

  # Convert to UTC if not already
  utc_time = time.respond_to?(:utc) ? time.utc : time
  iso_time = utc_time.iso8601

  (
    :span,
    time_ago_in_words(time) + " ago",  # Fallback for non-JS browsers
    class: "local-time-ago",
    data: {
      utc: iso_time
    }
  )
end

#permitted_filter_params(extra_keys: []) ⇒ Hash

Returns a sanitized hash of filter params safe for query links

Parameters:

  • extra_keys (Array<Symbol>) (defaults to: [])

    Additional permitted keys for specific contexts

Returns:

  • (Hash)

    Whitelisted params for building URLs



80
81
82
83
84
# File 'app/helpers/rails_error_dashboard/application_helper.rb', line 80

def permitted_filter_params(extra_keys: [])
  base_keys = RailsErrorDashboard::ErrorsController::FILTERABLE_PARAMS + %i[page per_page days]
  allowed_keys = base_keys + Array(extra_keys)
  params.permit(*allowed_keys).to_h.symbolize_keys
end

#platform_color_var(platform) ⇒ String

Returns platform-specific color class

Parameters:

  • platform (String)

    Platform name (ios, android, web, api)

Returns:

  • (String)

    CSS color variable



44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'app/helpers/rails_error_dashboard/application_helper.rb', line 44

def platform_color_var(platform)
  case platform&.downcase
  when "ios"
    "var(--platform-ios)"
  when "android"
    "var(--platform-android)"
  when "web"
    "var(--platform-web)"
  when "api"
    "var(--platform-api)"
  else
    "var(--text-color)"
  end
end

#platform_icon(platform) ⇒ String

Returns platform icon

Parameters:

  • platform (String)

    Platform name (ios, android, web, api)

Returns:

  • (String)

    Bootstrap icon class



62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'app/helpers/rails_error_dashboard/application_helper.rb', line 62

def platform_icon(platform)
  case platform&.downcase
  when "ios"
    "bi-apple"
  when "android"
    "bi-android2"
  when "web"
    "bi-globe"
  when "api"
    "bi-server"
  else
    "bi-question-circle"
  end
end

#severity_color(severity) ⇒ String

Returns Bootstrap color class for error severity Uses Catppuccin Mocha colors in dark theme via CSS variables

Parameters:

  • severity (Symbol)

    The severity level (:critical, :high, :medium, :low, :info)

Returns:

  • (String)

    Bootstrap color class (danger, warning, info, secondary)



7
8
9
10
11
12
13
14
15
16
17
18
19
20
# File 'app/helpers/rails_error_dashboard/application_helper.rb', line 7

def severity_color(severity)
  case severity&.to_sym
  when :critical
    "danger"   # Maps to --ctp-red in dark mode
  when :high
    "warning"  # Maps to --ctp-peach in dark mode
  when :medium
    "info"     # Maps to --ctp-blue in dark mode
  when :low
    "secondary" # Maps to --ctp-overlay1 in dark mode
  else
    "secondary"
  end
end

#severity_color_var(severity) ⇒ String

Returns CSS variable for severity color (for inline styles) Useful when you need to set background-color or color directly

Parameters:

  • severity (Symbol)

    The severity level

Returns:

  • (String)

    CSS variable reference



26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'app/helpers/rails_error_dashboard/application_helper.rb', line 26

def severity_color_var(severity)
  case severity&.to_sym
  when :critical
    "var(--ctp-red)"
  when :high
    "var(--ctp-peach)"
  when :medium
    "var(--ctp-blue)"
  when :low
    "var(--ctp-overlay1)"
  else
    "var(--ctp-overlay1)"
  end
end

#sortable_header(label, column) ⇒ String

Generates a sortable column header link

Parameters:

  • label (String)

    The column label to display

  • column (String)

    The column name to sort by

Returns:

  • (String)

    HTML safe link with sort indicator



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'app/helpers/rails_error_dashboard/application_helper.rb', line 90

def sortable_header(label, column)
  current_sort = params[:sort_by]
  current_direction = params[:sort_direction] || "desc"

  # Determine new direction: if clicking same column, toggle; otherwise default to desc
  new_direction = if current_sort == column
    current_direction == "asc" ? "desc" : "asc"
  else
    "desc"
  end

  # Choose icon based on current state
  icon = if current_sort == column
    current_direction == "asc" ? "" : ""
  else
    ""  # Unsorted indicator
  end

  # Preserve whitelisted filter params while adding sort params
  link_params = permitted_filter_params.merge(sort_by: column, sort_direction: new_direction)

  link_to errors_path(link_params), class: "text-decoration-none" do
    (:span, "#{label} ", class: current_sort == column ? "fw-bold" : "") +
    (:span, icon, class: "text-muted small")
  end
end