Module: Crm::WebhookLogsHelper

Defined in:
app/helpers/crm/webhook_logs_helper.rb

Overview

View helpers for the CRM webhook logs screens
(Crm::WebhookLogsController): state badges, resource links, payload
formatting, per-row actions, and dashboard stats.

Instance Method Summary collapse

Instance Method Details

#extract_payload_identifier(log) ⇒ ActiveSupport::SafeBuffer?

Extracts a quick identifier from the payload (email, tracking number,
transcript id, etc.) rendered as muted small text.

Parameters:

Returns:

  • (ActiveSupport::SafeBuffer, nil)

    the identifier markup, or nil
    when no identifier is found



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

def extract_payload_identifier(log)
  data = log.data || {}

  identifier = case log.provider
               when 'sendgrid'
                 data['email']
               when 'shipengine'
                 tracking = data.dig('data', 'tracking_number')
                 tracking.present? ? "Track: #{tracking}" : nil
               when 'oxylabs'
                 data['url']&.truncate(40)
               when 'assemblyai'
                 transcript_id = data['transcript_id']
                 transcript_id.present? ? "Transcript: #{transcript_id.truncate(20)}" : nil
               end

  # Fallback: try common fields
  identifier ||= data['email'] || data['tracking_number'] || data['id']&.to_s&.truncate(30)

  return nil if identifier.blank?

  tag.small(class: 'text-body-secondary') { identifier }
end

#webhook_log_actions(log, include_show_action: true, return_path: nil) ⇒ Array<ActiveSupport::SafeBuffer>

Builds the dropdown action links for a webhook log row, gated on the
log's state and the user's abilities.

Parameters:

  • log (WebhookLog)

    the webhook log

  • include_show_action (Boolean) (defaults to: true)

    whether to include the Show link

  • return_path (String, nil) (defaults to: nil)

    path to return to after the action

Returns:

  • (Array<ActiveSupport::SafeBuffer>)

    the action links



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'app/helpers/crm/webhook_logs_helper.rb', line 122

def webhook_log_actions(log, include_show_action: true, return_path: nil)
  links = []
  with_options(controller: 'crm/webhook_logs', return_path: return_path) do |defaults|
    # State-dependent actions
    links << link_to('Process', defaults.url_for(action: :do_process, id: log.id), data: { turbo_method: :post }, class: 'dropdown-item') if log.ready? || log.retry?

    links << link_to('Reprocess', defaults.url_for(action: :do_reprocess, id: log.id), data: { turbo_method: :post }, class: 'dropdown-item') if log.processed? || log.exception? || log.retry?

    links << link_to('Archive', defaults.url_for(action: :do_archive, id: log.id), data: { turbo_method: :post }, class: 'dropdown-item') if log.exception? || log.processed?

    # Standard actions
    links << link_to('Show', defaults.url_for(action: :show, id: log.id), class: 'dropdown-item') if include_show_action
    links << link_to('Edit', defaults.url_for(action: :edit, id: log.id), class: 'dropdown-item') if can?(:update, log)
    if can?(:destroy, log)
      links << link_to('Delete', defaults.url_for(action: :destroy, id: log.id),
                       data: { turbo_method: :delete, turbo_confirm: 'Delete this webhook log?' },
                       class: 'dropdown-item text-danger')
    end
  end
  links
end

#webhook_log_format_data(log) ⇒ ActiveSupport::SafeBuffer

Pretty-prints the request payload inside a styled block.

Parameters:

Returns:

  • (ActiveSupport::SafeBuffer)

    the formatted payload markup



148
149
150
151
152
# File 'app/helpers/crm/webhook_logs_helper.rb', line 148

def webhook_log_format_data(log)
  return tag.pre('No data', class: 'text-body-secondary') if log.data.blank?

  tag.pre(pretty_json_tag(log.data), class: 'bg-light p-3 rounded')
end

#webhook_log_format_response(log) ⇒ ActiveSupport::SafeBuffer

Pretty-prints the response payload inside a styled block.

Parameters:

Returns:

  • (ActiveSupport::SafeBuffer)

    the formatted response markup



158
159
160
161
162
# File 'app/helpers/crm/webhook_logs_helper.rb', line 158

def webhook_log_format_response(log)
  return tag.pre('No response', class: 'text-body-secondary') if log.response_data.blank?

  tag.pre(pretty_json_tag(log.response_data), class: 'bg-light p-3 rounded')
end

#webhook_log_provider_name(log) ⇒ String

Human-friendly provider name for a webhook log.

Parameters:

Returns:

  • (String)

    the titleized provider name



38
39
40
# File 'app/helpers/crm/webhook_logs_helper.rb', line 38

def webhook_log_provider_name(log)
  log.provider.to_s.titleize
end

Builds the resource cell for a webhook log row: a payload identifier
plus a link to the mapped resource (or a deleted marker), joined with
a line break.

Parameters:

Returns:

  • (String, ActiveSupport::SafeBuffer)

    '-' when nothing is available,
    otherwise the joined markup



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'app/helpers/crm/webhook_logs_helper.rb', line 49

def webhook_log_resource_link(log)
  parts = []

  # Always try to extract payload identifier first
  payload_id = extract_payload_identifier(log)
  parts << payload_id if payload_id.present?

  # If resource is mapped, add link
  if log.resource_type.present? && log.resource_id.present?
    resource = log.resource
    resource_link = if resource.nil?
                      tag.span(class: 'text-warning') { "#{log.resource_type} ##{log.resource_id} (deleted)" }
                    else
                      case log.resource_type
                      when 'CallRecord'
                        link_to "CallRecord ##{log.resource_id}", call_record_path(resource)
                      when 'Video'
                        link_to resource.title.truncate(30), video_path(resource)
                      when 'CatalogItem'
                        link_to "CatalogItem ##{log.resource_id}", catalog_item_path(resource)
                      when 'CommunicationRecipient'
                        link_to "Communication ##{resource.communication_id}", communication_path(resource.communication)
                      when 'Shipment'
                        link_to "Shipment ##{log.resource_id}", shipment_path(resource)
                      else
                        "#{log.resource_type} ##{log.resource_id}"
                      end
                    end
    parts << resource_link
  end

  return '-' if parts.empty?

  safe_join(parts, tag.br)
end

#webhook_log_state_badge(log) ⇒ ActiveSupport::SafeBuffer

Renders a Bootstrap badge describing a webhook log's state.

Parameters:

Returns:

  • (ActiveSupport::SafeBuffer)

    the badge markup



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'app/helpers/crm/webhook_logs_helper.rb', line 13

def webhook_log_state_badge(log)
  state = log.state.to_s
  badge_class = case state
                when 'pending' then 'bg-info'        # Awaiting callback
                when 'ready' then 'bg-primary'       # Callback received, ready to process
                when 'processing' then 'bg-warning'  # Currently processing
                when 'processed' then 'bg-success'   # Completed
                when 'retry' then 'bg-warning'       # Scheduled for retry
                when 'exception' then 'bg-danger'    # Failed
                when 'archived' then 'bg-secondary'  # Archived
                else 'bg-dark'
                end

  label = case state
          when 'pending' then 'Awaiting Callback'
          else state.titleize
          end

  tag.span(label, class: "badge #{badge_class}")
end

#webhook_log_statsHash{Symbol => Integer}

Aggregated counts for the webhook logs dashboard header.

Returns:

  • (Hash{Symbol => Integer})

    counts keyed by state bucket



167
168
169
170
171
172
173
174
175
176
# File 'app/helpers/crm/webhook_logs_helper.rb', line 167

def webhook_log_stats
  {
    total: WebhookLog.count,
    awaiting_callback: WebhookLog.where(state: 'pending').count,
    ready_to_process: WebhookLog.where(state: %w[ready retry]).count,
    processed: WebhookLog.where(state: 'processed').count,
    failed: WebhookLog.where(state: 'exception').count,
    today: WebhookLog.where(created_at: Time.current.beginning_of_day..).count
  }
end