Module: ForestAdminDatasourceZendesk::Plugins::CreateTicketWithNotification::FormBuilder

Defined in:
lib/forest_admin_datasource_zendesk/plugins/create_ticket_with_notification/form_builder.rb

Constant Summary collapse

FieldType =
ForestAdminDatasourceCustomizer::Decorators::Action::Types::FieldType
NO_TEMPLATE =
'No template'.freeze
TOKEN_RE =
/\{\{\s*record\.([a-zA-Z_][a-zA-Z0-9_]*)\s*\}\}/

Class Method Summary collapse

Class Method Details

.body_fields(opts) ⇒ Object



28
29
30
31
32
33
34
35
36
# File 'lib/forest_admin_datasource_zendesk/plugins/create_ticket_with_notification/form_builder.rb', line 28

def body_fields(opts)
  fields = [requester_field(opts[:requester_email_default]),
            subject_field(opts[:default_subject]),
            message_field(opts[:default_message], opts[:email_templates])]
  fields << priority_field unless present?(opts[:priority_override])
  fields << type_field unless present?(opts[:type_override])
  fields << internal_note_field if opts[:show_internal_note]
  fields
end

.build(opts) ⇒ Object

ActionCollectionDecorator rejects forms that mix Page elements with non-Page elements, so each mode (flat / wizard) stays homogeneous.



16
17
18
19
20
21
22
23
24
25
26
# File 'lib/forest_admin_datasource_zendesk/plugins/create_ticket_with_notification/form_builder.rb', line 16

def build(opts)
  body = body_fields(opts)
  return body if opts[:email_templates].empty?

  [
    { type: 'Layout', component: 'Page', next_button_label: 'Continue',
      elements: [template_field(opts[:email_templates])] },
    { type: 'Layout', component: 'Page', previous_button_label: 'Back',
      elements: body }
  ]
end

.fetch_record(context) ⇒ Object



122
123
124
125
126
127
128
129
# File 'lib/forest_admin_datasource_zendesk/plugins/create_ticket_with_notification/form_builder.rb', line 122

def fetch_record(context)
  context.get_record([]) || {}
rescue StandardError => e
  ForestAdminDatasourceZendesk.logger.warn(
    "[forest_admin_datasource_zendesk] failed to fetch record for token interpolation: #{e.class}: #{e.message}"
  )
  {}
end

.internal_note_fieldObject



77
78
79
80
81
# File 'lib/forest_admin_datasource_zendesk/plugins/create_ticket_with_notification/form_builder.rb', line 77

def internal_note_field
  { type: FieldType::BOOLEAN, label: 'Send as internal note',
    description: 'When checked, the first comment is private and no email is sent to the requester.',
    default_value: false }
end

.interpolate(template, record, escape_html:) ⇒ Object

Message ships as html_body — unescaped ‘<` or `&` from a record value would break the outbound email or smuggle markup into it.



133
134
135
136
137
138
139
140
141
# File 'lib/forest_admin_datasource_zendesk/plugins/create_ticket_with_notification/form_builder.rb', line 133

def interpolate(template, record, escape_html:)
  template.gsub(TOKEN_RE) do
    key = ::Regexp.last_match(1)
    value = record[key]
    next '' if value.nil?

    escape_html ? CGI.escapeHTML(value.to_s) : value.to_s
  end
end

.message_field(default_message, templates) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
# File 'lib/forest_admin_datasource_zendesk/plugins/create_ticket_with_notification/form_builder.rb', line 56

def message_field(default_message, templates)
  field = { type: FieldType::STRING, label: 'Message', widget: 'RichText', is_required: true,
            description: 'Sent as the ticket\'s first comment (HTML). Public comments trigger the ' \
                         'default Zendesk notification email to the requester.' }
  return field.merge(default_value: template_default(default_message, escape_html: true)) if templates.empty?

  # `value:` (not `default_value:`) — drop_default runs once (data
  # key sticks after the first render); drop_deferred re-evaluates
  # on every fetch, so Template changes re-fire the message proc.
  field.merge(value: message_value(templates))
end

.message_value(templates) ⇒ Object

Returns nil unless Template was just changed, so set_watch_changes carries over the user’s current Message edits between renders.



107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/forest_admin_datasource_zendesk/plugins/create_ticket_with_notification/form_builder.rb', line 107

def message_value(templates)
  by_title = templates.to_h { |t| [t[:title], t[:content].to_s] }
  lambda do |context|
    return nil unless context.field_changed?('Template')

    title = context.get_form_value('Template')
    return '' if title == NO_TEMPLATE

    content = by_title[title].to_s
    return content unless content.match?(TOKEN_RE)

    interpolate(content, fetch_record(context), escape_html: true)
  end
end

.present?(value) ⇒ Boolean

Returns:

  • (Boolean)


143
144
145
# File 'lib/forest_admin_datasource_zendesk/plugins/create_ticket_with_notification/form_builder.rb', line 143

def present?(value)
  !value.nil? && value.to_s != ''
end

.priority_fieldObject



68
69
70
71
# File 'lib/forest_admin_datasource_zendesk/plugins/create_ticket_with_notification/form_builder.rb', line 68

def priority_field
  { type: FieldType::ENUM, label: 'Priority',
    enum_values: TicketEnums::PRIORITY, default_value: 'normal' }
end

.requester_default(value) ⇒ Object



83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/forest_admin_datasource_zendesk/plugins/create_ticket_with_notification/form_builder.rb', line 83

def requester_default(value)
  return nil if value.nil?
  return template_default(value, escape_html: false) if value.is_a?(String)

  lambda do |context|
    record = fetch_record(context)
    record.empty? ? nil : value.call(record)
  rescue StandardError => e
    ForestAdminDatasourceZendesk.logger.warn(
      "[forest_admin_datasource_zendesk] requester_email_default resolver raised: #{e.class}: #{e.message}"
    )
    nil
  end
end

.requester_field(default) ⇒ Object



38
39
40
41
42
# File 'lib/forest_admin_datasource_zendesk/plugins/create_ticket_with_notification/form_builder.rb', line 38

def requester_field(default)
  { type: FieldType::STRING, label: 'Requester email', is_required: true,
    description: 'Email of the Zendesk requester. Pre-filled from the selected record when available.',
    default_value: requester_default(default) }
end

.subject_field(default_subject) ⇒ Object



51
52
53
54
# File 'lib/forest_admin_datasource_zendesk/plugins/create_ticket_with_notification/form_builder.rb', line 51

def subject_field(default_subject)
  { type: FieldType::STRING, label: 'Subject', is_required: true,
    default_value: template_default(default_subject, escape_html: false) }
end

.template_default(template, escape_html:) ⇒ Object



98
99
100
101
102
103
# File 'lib/forest_admin_datasource_zendesk/plugins/create_ticket_with_notification/form_builder.rb', line 98

def template_default(template, escape_html:)
  return nil unless present?(template)
  return template unless template.match?(TOKEN_RE)

  ->(context) { interpolate(template, fetch_record(context), escape_html: escape_html) }
end

.template_field(templates) ⇒ Object



44
45
46
47
48
49
# File 'lib/forest_admin_datasource_zendesk/plugins/create_ticket_with_notification/form_builder.rb', line 44

def template_field(templates)
  { type: FieldType::ENUM, label: 'Template', is_required: true,
    enum_values: [NO_TEMPLATE] + templates.map { |t| t[:title] },
    default_value: NO_TEMPLATE,
    description: 'Pick a template to pre-fill the Message on the next page.' }
end

.type_fieldObject



73
74
75
# File 'lib/forest_admin_datasource_zendesk/plugins/create_ticket_with_notification/form_builder.rb', line 73

def type_field
  { type: FieldType::ENUM, label: 'Type', enum_values: TicketEnums::TYPE }
end