Module: CmAdmin::Models::DslMethod

Extended by:
ActiveSupport::Concern
Defined in:
lib/cm_admin/models/dsl_method.rb

Instance Method Summary collapse

Instance Method Details

#action_group(name, icon_name: 'fa fa-list', icon_style: nil) ⇒ Object

Create a new group for actions

Examples:

Creating a group

group 'Basic Information', icon_name: 'fa fa-list', icon_style: 'color: #000000' do
  custom_action 'Edit', verb: 'get', display_type: :button, icon_name: 'fa fa-edit', icon_style: 'color: #000000' do
    @posts = Post.all
  end
end

Parameters:

  • name (String)

    the name of group

  • icon_name (String) (defaults to: 'fa fa-list')

    the icon name of group, follow font-awesome icon name

  • icon_style (String) (defaults to: nil)

    the icon style of group, follow font-awesome icon style, example: “–fa-primary-color: #fecb3e; –fa-secondary-color: #e63b7a;”



298
299
300
301
302
# File 'lib/cm_admin/models/dsl_method.rb', line 298

def action_group(name, icon_name: 'fa fa-list', icon_style: nil)
  @current_group = { name:, icon_name:, icon_style: }
  yield
  @current_group = nil
end

#alert_box(header: nil, body: nil, type: nil, icon_name: nil, partial: nil, display_if: ->(_arg) { true }, html_attrs: {}) ⇒ Object

Note:

Alerts cannot be placed inside nested sections. If added within a nested section, the alert will appear on the wrapped cm_show_section.

Note:

Only the specified types (info, success, danger, warning) are supported. Any other type will default to a standard div.

Adds a new alert to the current section.

Examples:

Basic info alert

alert_box(header: "Information", body: "This is an informational message.", type: :info)

Basic info alert with custom body

alert_box(header: "Information", body: "This is an informational message. <br>This is a break text", type: :info)

Alert with custom partial

alert_box(partial: "/users/sessions/alert", display_if: ->(arg) { arg.present? })

Alert with conditional display and custom HTML attributes

alert_box(
  header: "Warning",
  body: "Please review your submission.",
  type: :warning,
  display_if: ->(user) { user.submissions.any?(&:incomplete?) },
  html_attrs: { id: "submission-warning", data: { turbo_frame: "warnings" } }
)

Parameters:

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

    The title text for the alert.

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

    A string to display as the body of the alert.

  • type (Symbol, nil) (defaults to: nil)

    The type of alert. Accepts one of the following symbols: :info, :success, :danger, :warning.

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

    The path to a custom partial or HTML for the alert.

  • display_if (Proc, nil) (defaults to: ->(_arg) { true })

    A lambda function that determines whether the alert should be shown. Should return a boolean.

  • html_attrs (Hash) (defaults to: {})

    Additional HTML attributes to apply to the alert. Has no effect on partials.

See Also:



534
535
536
# File 'lib/cm_admin/models/dsl_method.rb', line 534

def alert_box(header: nil, body: nil, type: nil, icon_name: nil, partial: nil, display_if: ->(_arg) { true }, html_attrs: {})
  @current_action.alerts << CmAdmin::Models::Alert.new(header:, body:, type:, icon_name:, partial:, display_if:, html_attrs:)
end

#all_db_columns(options = {}) ⇒ Object

Get all columns for a model for index layout.

Examples:

Getting all columns

all_db_columns(exclude: ['id'])

Parameters:

  • exclude (Array)

    the array of fields to exclude



277
278
279
280
281
282
283
284
285
286
# File 'lib/cm_admin/models/dsl_method.rb', line 277

def all_db_columns(options = {})
  field_names = instance_variable_get(:@ar_model)&.columns&.map { |x| x.name.to_sym }
  if options.include?(:exclude) && field_names
    excluded_fields = ([] << options[:exclude]).flatten.map(&:to_sym)
    field_names -= excluded_fields
  end
  field_names.each do |field_name|
    column field_name
  end
end

#bulk_action(name: nil, display_name: nil, display_if: ->(_) { true }, redirection_url: nil, icon_name: nil, verb: nil, display_type: nil, modal_configuration: {}, route_type: nil, partial: nil, icon_style: nil, execution_mode: :individual, success_message: ->(_arg) { nil }, error_message: ->(_arg) { nil }, alert_type: :banner, &block) ⇒ Object

Create a new bulk action for model

Examples:

Creating a bulk action

bulk_action name: 'approve', display_name: 'Approve', display_if: lambda { |arg| arg.draft? }, redirection_url: '/posts', icon_name: 'fa-regular fa-circle-check', verb: :patch, display_type: :modal, modal_configuration: { title: 'Approve Post', description: 'Are you sure you want approve this post', confirmation_text: 'Approve' }, execution_mode: :individual do
  posts = ::Post.where(id: params[:ids])
  posts.each(&:approved!)
  posts
end

Creating a bulk action with form modal

bulk_action name: 'approve', display_name: 'Approve', display_if: lambda { |arg| arg.draft? }, redirection_url: '/posts', icon_name: 'fa-regular fa-circle-check', verb: :patch, display_type: :form_modal, modal_configuration: { title: 'Approve Post', confirmation_text: 'Approve' }, execution_mode: :individual do
 form do
   cm_section 'Comment' do
     form_field :comment
   end
 end
 on_submit do |id|
   post = ::Post.find(id)
   post.approved!
   post
 end
end

Parameters:

  • name (String) (defaults to: nil)

    the name of action

  • display_name (String) (defaults to: nil)

    the display name of action

  • display_if (Proc) (defaults to: ->(_) { true })

    A lambda that takes the current object and return true or false

  • redirection_url (String) (defaults to: nil)

    the redirection url of action

  • icon_name (String) (defaults to: nil)

    the icon name of action, follow font-awesome icon name

  • verb (String) (defaults to: nil)

    the verb of action, get, post, put, patch or delete

  • display_type (Symbol) (defaults to: nil)

    the display type of action, :page, :modal, :form_modal, :button, :icon_only[deprecated]

  • modal_configuration (Hash) (defaults to: {})

    the configuration of modal

  • route_type (String) (defaults to: nil)

    the route type of action, member, collection

  • partial (String) (defaults to: nil)

    the partial path of action

  • execution_mode (Symbol) (defaults to: :individual)

    the types of execution mode are :bulk, :individual [default :individual]

  • alert_type (Symbol) (defaults to: :banner)

    the type of success alert to show. Choices are :flash and :banner. This is only for success messages.



429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
# File 'lib/cm_admin/models/dsl_method.rb', line 429

def bulk_action(name: nil, display_name: nil, display_if: ->(_) { true }, redirection_url: nil, icon_name: nil,
                verb: nil, display_type: nil, modal_configuration: {}, route_type: nil, partial: nil, icon_style: nil,
                execution_mode: :individual, success_message: ->(_arg) { nil }, error_message: ->(_arg) { nil }, alert_type: :banner, &block)
  bulk_action = CmAdmin::Models::BulkAction.new(
    name:, display_name:, display_if:, modal_configuration:,
    redirection_url:, icon_name:, action_type: :bulk_action,
    execution_mode:, verb:, display_type:, route_type:, partial:,
    icon_style:, success_message:, error_message:, alert_type:, &block
  )
  if display_type == :form_modal
    @action = bulk_action
    @current_action = @action
    @current_action.verb = :post
    @available_actions << @action
    define_singleton_method(:on_submit) do |&code_block|
      @action.code_block = code_block
    end
    yield
  else
    @available_actions << bulk_action
  end
end

#cm_edit(display_name: 'Edit', page_title: nil, page_description: nil, partial: nil, redirect_to: nil, display_if: ->(_arg) { true }) ⇒ Object

Create a form for edit action

Create a form field for form
@param form_field [String] the name of field
@param input_type [Symbol] the type of field, +:integer+, +:decimal+, +:string+, +:single_select+, +:multi_select+, +:date+, +:date_time+, +:text+, +:switch+, +:custom_single_select+, +:checkbox_group+, +:radio_button+, +:custom_string+, +:custom_date+, +:radio_button_group+
@param is_required [Boolean] the field is required or not, or can be set automatically based on model validation
@param collection [Array] the collection of field, use with single_select or multi_select
For understanding difference between switch and check_box, refer to https://blog.uxtweak.com/checkbox-vs-toggle-switch/

Examples:

Editing page with a redirect

cm_edit(display_name: 'Edit Post', page_title: "Edit Post", page_description: 'Enter all details to edit Post', redirect_to: ->(current_object) { "/pages/#{current_object.id}" }) do
  cm_section 'Details' do
    form_field :title, input_type: :string
    form_field :body, input_type: :rich_text
  end
end

Parameters:

  • display_name (String) (defaults to: 'Edit')

    the display/button name of page

  • page_title (String) (defaults to: nil)

    or [Symbol] the title of page, if symbol passed, it will be a method name on model

  • page_description (String) (defaults to: nil)

    the description of page

  • partial (String) (defaults to: nil)

    the partial path of page

  • redirect_to (Proc, nil) (defaults to: nil)

    A lambda that takes the current object and redirect to path after update



74
75
76
77
78
79
80
# File 'lib/cm_admin/models/dsl_method.rb', line 74

def cm_edit(display_name: 'Edit', page_title: nil, page_description: nil, partial: nil, redirect_to: nil, display_if: ->(_arg) { true })
  @current_action = CmAdmin::Models::Action.find_by(self, name: 'edit')
  page_title ||= "Edit #{formatted_name.pluralize}"
  @current_action.set_values(display_name, page_title, page_description, partial, redirect_to)
  @current_action.display_if = display_if
  yield
end

#cm_index(display_name: nil, page_title: nil, page_description: nil, partial: nil, view_type: :table) ⇒ Object

Create a table for index page with pagination.

Examples:

Index page

cm_index do
  page_title 'Post'
  column :title
  column :created_at, field_type: :date, format: '%d %b, %Y'
  column :updated_at, field_type: :date, format: '%d %b, %Y', header: 'Last Updated At'
end

Parameters:

  • display_name (String) (defaults to: nil)

    the display name of page

  • page_title (String) (defaults to: nil)

    the title of page

  • page_description (String) (defaults to: nil)

    the description of page

  • partial (String) (defaults to: nil)

    the partial path of page

  • view_type (Symbol) (defaults to: :table)

    view type of page :table, :card or :kanban



21
22
23
24
25
26
# File 'lib/cm_admin/models/dsl_method.rb', line 21

def cm_index(display_name: nil, page_title: nil, page_description: nil, partial: nil, view_type: :table)
  @current_action = CmAdmin::Models::Action.find_by(self, name: 'index')
  page_title ||= formatted_name.pluralize
  @current_action.set_values(display_name, page_title, page_description, partial, view_type)
  yield
end

#cm_new(display_name: 'Add', page_title: nil, page_description: nil, partial: nil, redirect_to: nil, allow_create_action: -> { true }) ⇒ Object

Create a form for new action

Create a form field for form
@param form_field [String] the name of field
@param input_type [Symbol] the type of field, +:integer+, +:decimal+, +:string+, +:single_select+, +:multi_select+, +:date+, +:date_time+, +:text+, +:switch+, +:custom_single_select+, +:checkbox_group+, +:radio_button+, +:custom_string+, +:custom_date+, +:radio_button_group+
@param is_required [Boolean] the field is required or not, or can be set automatically based on model validation
@param collection [Array] the collection of field, use with single_select or multi_select
For understanding difference between switch and check_box, refer to https://blog.uxtweak.com/checkbox-vs-toggle-switch/

Examples:

Creating a new page with a redirect

cm_new(display_name: 'Add Post', page_title: "Add Post", page_description: 'Enter all details to add Post', redirect_to: ->(current_object) { "/pages/#{current_object.id}" }) do
  cm_section 'Details' do
    form_field :title, input_type: :string
    form_field :body, input_type: :rich_text
  end
end

Parameters:

  • display_name (String) (defaults to: 'Add')

    the display/button name of page

  • page_title (String) (defaults to: nil)

    the title of page

  • page_description (String) (defaults to: nil)

    the description of page

  • partial (String) (defaults to: nil)

    the partial path of page

  • redirect_to (Proc, nil) (defaults to: nil)

    A lambda that takes the current object and redirect to path after create

  • allow_create_action (Proc) (defaults to: -> { true })

    A lambda that determines if the Add button (create/new action) should be shown



104
105
106
107
108
109
110
# File 'lib/cm_admin/models/dsl_method.rb', line 104

def cm_new(display_name: 'Add', page_title: nil, page_description: nil, partial: nil, redirect_to: nil, allow_create_action: -> { true })
  @current_action = CmAdmin::Models::Action.find_by(self, name: 'new')
  page_title ||= "Add #{formatted_name.pluralize}"
  @current_action.set_values(display_name, page_title, page_description, partial, redirect_to)
  @current_action.allow_create_action = allow_create_action
  yield
end

#cm_section(section_name, display_if: nil, col_size: nil, html_attrs: nil, partial: nil, dynamic_fields: nil, &block) ⇒ Object

Examples:

Creating a section

cm_section('Basic Information', display_if: ->(current_object) { current_object.name == 'John' }, col_size: 6, html_attrs: { class: 'section-class' }) do
  field :title, input_type: :string
end


208
209
210
211
212
# File 'lib/cm_admin/models/dsl_method.rb', line 208

def cm_section(section_name, display_if: nil, col_size: nil, html_attrs: nil, partial: nil, dynamic_fields: nil, &block)
  @available_fields[@current_action.name.to_sym] ||= []
  @available_fields[@current_action.name.to_sym] << CmAdmin::Models::Section.new(section_name, @current_action, @model, display_if, html_attrs,
                                                                                 col_size, partial, dynamic_fields, &block)
end

#cm_show(display_name: nil, page_title: nil, page_description: nil, partial: nil) ⇒ Object

Create a view for show page

Examples:

Showing page

cm_show page_title: :title do
  tab :profile, '' do
    cm_section 'Post Details' do
      field :title
      field :body, field_type: :rich_text
      field :is_featured
      field :status, field_type: :tag, tag_class: STATUS_TAG_COLOR
    end
  end
end

Parameters:

  • display_name (String) (defaults to: nil)

    the display name of page

  • page_title (String | Symbol) (defaults to: nil)

    the title of page, if symbol passed, it will be a method name on model

  • page_description (String) (defaults to: nil)

    the description of page

  • partial (String) (defaults to: nil)

    the partial path of page



46
47
48
49
50
51
# File 'lib/cm_admin/models/dsl_method.rb', line 46

def cm_show(display_name: nil, page_title: nil, page_description: nil, partial: nil)
  @current_action = CmAdmin::Models::Action.find_by(self, name: 'show')
  page_title ||= formatted_name.pluralize
  @current_action.set_values(display_name, page_title, page_description, partial)
  yield
end

#cm_show_section(section_name, display_if: nil, html_attrs: nil, partial: nil, &block) ⇒ Object

Deprecated.

Use #cm_section instead of this method



215
216
217
# File 'lib/cm_admin/models/dsl_method.rb', line 215

def cm_show_section(section_name, display_if: nil, html_attrs: nil, partial: nil, &block)
  cm_section(section_name, display_if:, html_attrs:, partial:, &block)
end

#column(field_name, options = {}) ⇒ Object

Create a new column on index layout.

Examples:

Creating a column

column('name', field_type: :string)

Parameters:

  • field_name (String)

    the name of field

  • field_type (Symbol)

    the type of field, :string, :text, :image, :date, :rich_text, :time, :integer, :decimal, :custom, :datetime, :money, :money_with_symbol, :link, :association, :enum, :tag, :attachment, :drawer

  • header (String)

    the header of field

  • format (String)

    the format of field for date field

  • helper_method (Symbol)

    the helper method for field, should be defined in custom_helper.rb file, will take two arguments, record and field_name

  • export_method (Symbol)

    the export method for field, should be defined in model, will take no arguments

  • height (Integer)

    the height of field for image field

  • width (Integer)

    the width of field for image field



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
# File 'lib/cm_admin/models/dsl_method.rb', line 234

def column(field_name, options = {})
  return unless @current_action.instance_of?(CmAdmin::Models::Action)

  @available_fields[@current_action.name.to_sym] ||= []
  if @available_fields[@current_action.name.to_sym].select(&:lockable).size.positive? && options[:lockable]
    raise 'Only one column can be locked in a table.'
  end

  if @available_fields[@current_action.name.to_sym].select(&:kanban_image).size.positive? && options[:kanban_image]
    raise 'Only one column can be kanban image in a card view.'
  end

  if @available_fields[@current_action.name.to_sym].select(&:kanban_footer).size >= 2 && options[:kanban_footer]
    raise 'Only two columns can be kanban footer in a card view.'
  end

  duplicate_columns = @available_fields[@current_action.name.to_sym].filter do |x|
    !x.field_name.is_a?(Array) && x.field_name.to_sym == field_name
  end
  terminate = false

  if duplicate_columns.size.positive?
    duplicate_columns.each do |column|
      if options[:field_type].to_s != 'association'
        terminate = true
      elsif options[:field_type].to_s == 'association' && column.association_name.to_s == options[:association_name].to_s
        terminate = true
      end
    end
  end

  return if terminate

  add_to_filter = options[:add_to_filter].nil? || options[:add_to_filter]
  generate_filter_for_columns(field_name, add_to_filter: add_to_filter)

  @available_fields[@current_action.name.to_sym] << CmAdmin::Models::Column.new(field_name, options)
end

#custom_action(name: nil, page_title: nil, page_description: nil, display_name: nil, verb: nil, layout: nil, layout_type: nil, partial: nil, path: nil, display_type: nil, redirect_to: nil, modal_configuration: {}, url_params: {}, display_if: ->(_) { true }, route_type: nil, icon_name: 'fa fa-th-large', icon_style: nil, group_name: nil, success_message: ->(_) { nil }, error_message: ->(_, _) { nil }, alert_type: :flash, &block) ⇒ Object

Create a new custom action for model custom_action name: ‘change_password’, route_type: ‘member’, verb: ‘get’, icon_name: ‘fa-solid fa-key’, page_title: ‘Change Password’, path: ‘:id/change_password’, display_type: :form_modal do

form do
  cm_section 'Password Details' do
    form_field :username
    form_field :age_group_id, input_type: :single_select, helper_method: :age_group_type_collection
    form_field :cultural_background_ids, input_type: :multi_select, helper_method: :cultural_background_type_collection
    form_field :language, input_type: :single_select, helper_method: :language_collection
  end
end
on_submit do
  @customer = ::Customer.find(params[:id])
  @customer.update!(gender_id: params[:customer][:age_group_id])
  @customer
end

end Note: The form block is mandatory for form modal and form page. Params fields will always be under the parent class, for example: params[:age_group_id]

Examples:

Creating a custom action with modal

custom_action name: 'approve', route_type: 'member', verb: 'patch', icon_name: 'fa-regular fa-circle-check', path: ':id/approve', display_type: :modal, display_if: lambda(&:draft?), modal_configuration: { title: 'Approve Post', description: 'Are you sure you want approve this post', confirmation_text: 'Approve' } do
  post = ::Post.find(params[:id])
  post.approved!
  post
end

Creating a custom action with button

custom_action name: 'approve', route_type: 'member', verb: 'patch', icon_name: 'fa-regular fa-circle-check', path: ':id/approve', display_type: :button, display_if: lambda(&:draft?), group_name: 'my action' do
  post = ::Post.find(params[:id])
  post.approved!
  post
end

Creating a custom action with form modal and form page

Parameters:

  • name (String) (defaults to: nil)

    the name of action

  • page_title (String) (defaults to: nil)

    the title of page

  • page_description (String) (defaults to: nil)

    the description of page

  • display_name (String) (defaults to: nil)

    the display name of action

  • verb (String) (defaults to: nil)

    the verb of action, get, post, put, patch or delete

  • layout (String) (defaults to: nil)

    the layout of action

  • layout_type (String) (defaults to: nil)

    the layout type of action, cm_association_index, cm_association_show

  • partial (String) (defaults to: nil)

    the partial path of action

  • path (String) (defaults to: nil)

    the path of action

  • display_type (Symbol) (defaults to: nil)

    the display type of action, :button, :modal , :form_modal, :form_page, :page, :icon_only

  • modal_configuration (Hash) (defaults to: {})

    the configuration of modal

  • url_params (Hash) (defaults to: {})

    the url params of action

  • display_if (Proc) (defaults to: ->(_) { true })

    A lambda that takes the current object and return true or false

  • route_type (String) (defaults to: nil)

    the route type of action, member, collection

  • icon_name (String) (defaults to: 'fa fa-th-large')

    the icon name of action, follow font-awesome icon name

  • icon_style (String) (defaults to: nil)

    the icon style of action, follow font-awesome icon style, example: “–fa-primary-color: #fecb3e; –fa-secondary-color: #e63b7a;”

  • group_name (String) (defaults to: nil)

    the group name of action, all the action with same group name will be grouped together in dropdown.

  • alert_type (Symbol) (defaults to: :flash)

    the type of success alert to show. Choices are :flash and :banner. This is only for success messages.



356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
# File 'lib/cm_admin/models/dsl_method.rb', line 356

def custom_action(name: nil, page_title: nil, page_description: nil, display_name: nil, verb: nil, layout: nil,
                  layout_type: nil, partial: nil, path: nil, display_type: nil, redirect_to: nil,
                  modal_configuration: {}, url_params: {}, display_if: ->(_) { true }, route_type: nil,
                  icon_name: 'fa fa-th-large', icon_style: nil, group_name: nil, success_message: ->(_) { nil },
                  error_message: ->(_, _) { nil }, alert_type: :flash, &block)
  @action = CmAdmin::Models::CustomAction.new(
    page_title:, page_description:,
    name:, display_name:, verb:, layout:,
    layout_type:, partial:, path:,
    parent: current_action.name, display_type:, display_if:,
    redirect_to:, action_type: :custom, route_type:,
    icon_name:, icon_style:, modal_configuration:,
    model_name: self.name, url_params:, group_name:, alert_type:,
    success_message:, error_message:, &block
  )

  if @current_group
    @action.group_name = @current_group[:name]
    @action.group_icon_name = @current_group[:icon_name]
    @action.group_icon_style = @current_group[:icon_style]
  end

  if %i[form_page form_modal].include?(display_type)
    @current_action = @action
    @available_actions << @action

    define_singleton_method(:on_submit) do |&code_block|
      if display_type == :form_modal
        @action.code_block = code_block
      else
        @action.verb = :get
        generate_update_action(name, path, verb, redirect_to, route_type, success_message, error_message, &code_block)
      end
    end

    yield
  else
    @available_actions << @action
  end
end

#eager_load_associations(associations = []) ⇒ Object

Configure Eager load associations for action This will help us avoid N+1 queries

Examples:

Eager load associations

eager_load_assocations [:association_name]
eager_load_assocations [:constant]


597
598
599
# File 'lib/cm_admin/models/dsl_method.rb', line 597

def eager_load_associations(associations = [])
  @current_action.eager_load_associations = associations if @current_action
end

#filter(db_column_name, filter_type = nil, options = {}) ⇒ Object

Create a new filter for model

Examples:

Creating a filter

filter('name', :search)
filter('created_at', :date)
filter('status', :single_select, collection: ['draft', 'published'])
filter('status', :multi_select,  helper_method: 'status_collection')
filter('age', :range)

Creating a filter with default value lambda

filter('status', :single_select, collection: ['draft', 'published'], default_value: -> { 'draft' })
filter('category', :multi_select, collection: ['A', 'B', 'C'], default_value: -> { ['A', 'B'] })
filter('created_at', :date, default_value: -> { "#{30.days.ago.to_date} to #{Date.today}" })
filter('user_id', :single_select, helper_method: 'user_collection', default_value: -> { Current.user.id })

Parameters:

  • db_column_name (String)

    the name of column

  • filter_type (String) (defaults to: nil)

    the type of filter, :date, :multi_select, :range, :search, :single_select

  • placeholder (String)

    the placeholder of filter

  • helper_method (String)

    the helper method for filter, should be defined in custom_helper.rb file

  • filter_with (Symbol)

    filter with scope name on model

  • active_by_default (Boolean)

    make filter active by default

  • collection (Array)

    the collection of filter, use with single_select or multi_select

  • default_value (Proc)

    the default value lambda for the filter when no filter params are provided



472
473
474
475
# File 'lib/cm_admin/models/dsl_method.rb', line 472

def filter(db_column_name, filter_type = nil, options = {})
  filter_type = default_filter_type(db_column_name, filter_type)
  @filters << CmAdmin::Models::Filter.new(db_column_name:, filter_type:, options:)
end

#kanban_view(column_name, exclude: [], only: []) ⇒ Object

Set kanban view for current action

Parameters:

  • column_name (String)

    the name of column

  • exclude (Array) (defaults to: [])

    the array of fields to exclude

  • only (Array) (defaults to: [])

    the array of fields to include



132
133
134
135
136
137
138
# File 'lib/cm_admin/models/dsl_method.rb', line 132

def kanban_view(column_name, exclude: [], only: [])
  return unless @current_action

  @current_action.kanban_attr[:column_name] = column_name
  @current_action.kanban_attr[:exclude] = exclude
  @current_action.kanban_attr[:only] = only
end

#page_description(description) ⇒ Object

Set page description for current action

Parameters:

  • description (String)

    the description of page



122
123
124
125
126
# File 'lib/cm_admin/models/dsl_method.rb', line 122

def page_description(description)
  return unless @current_action

  @current_action.page_description = description
end

#page_title(title) ⇒ Object

Set page title for current action

Parameters:

  • title (String)

    the title of page



114
115
116
117
118
# File 'lib/cm_admin/models/dsl_method.rb', line 114

def page_title(title)
  return unless @current_action

  @current_action.page_title = title
end

#row(display_if: nil, html_attrs: nil, &block) ⇒ Object

Create a new row on page or form.

Examples:

Creating a row

row(display_if: ->(current_object) { current_object.name == 'John' }, html_attrs: { class: 'row-class' }) do
  cm_section 'Details' do
    form_field :title, input_type: :string
  end
end

Parameters:

  • display_if (Proc) (defaults to: nil)

    A lambda that takes the current object and return true or false

  • html_attrs (Hash) (defaults to: nil)

    A hash that contains html attributes



193
194
195
196
# File 'lib/cm_admin/models/dsl_method.rb', line 193

def row(display_if: nil, html_attrs: nil, &block)
  @available_fields[@current_action.name.to_sym] ||= []
  @available_fields[@current_action.name.to_sym] << CmAdmin::Models::Row.new(@current_action, @model, display_if, html_attrs, &block)
end

#scope_list(scopes = []) ⇒ Object

Set scopes for current action

Parameters:

  • scopes (Array) (defaults to: [])

    the array of scopes



142
143
144
145
146
# File 'lib/cm_admin/models/dsl_method.rb', line 142

def scope_list(scopes = [])
  return unless @current_action

  @current_action.scopes = scopes
end

#sort_column(column = :created_at) ⇒ Object

@deprecated: use #sortable_columns instead of this method Set sort column for filters

Examples:

Setting sort column

sort_column(:created_at)

Parameters:

  • column (Symbol) (defaults to: :created_at)

    the column name



496
497
498
# File 'lib/cm_admin/models/dsl_method.rb', line 496

def sort_column(column = :created_at)
  @current_action.sort_column = column.to_sym if @current_action
end

#sort_direction(direction = :desc) ⇒ Object

@deprecated: use #sortable_columns instead of this method Set sort direction for filters

Examples:

Setting sort direction

sort_direction(:asc)

Parameters:

  • direction (Symbol) (defaults to: :desc)

    the direction of sort, :asc, :desc



482
483
484
485
486
487
488
489
# File 'lib/cm_admin/models/dsl_method.rb', line 482

def sort_direction(direction = :desc)
  unless CmAdmin::Models::Action::VALID_SORT_DIRECTION.include?(direction.to_sym.downcase)
    raise ArgumentError,
          "Select a valid sort direction like #{CmAdmin::Models::Action::VALID_SORT_DIRECTION.join(' or ')} instead of #{direction}"
  end

  @current_action.sort_direction = direction.to_sym if @current_action
end

#sortable_columns(columns) ⇒ Object

Note:

default sort direction will be ascending

Configure sortable columns for model verified_at is a JSONB column, so we need to specify the sort_datatype

sortable_columns([{column: 'id', display_name: 'ID', default: true, default_direction: 'desc'},
                  {column: 'updated_at', display_name: 'Last Updated At'},
                  {column: 'verified_at', display_name: 'Verified At', sort_datatype: 'TIMESTAMP'}])

Examples:

Sortable Columns

sortable_columns([{column: 'id', display_name: 'ID'},
                  {column: 'updated_at', display_name: 'Last Updated At'}])

Sortable Columns with default column and direction

Sortable Columns with display_if condition

sortable_columns([{column: 'id', display_name: 'ID'},
                  {column: 'created_at', display_name: 'Created At', display_if: ->(obj) { current_user.admin? }}])

Sortable Columns with custom sort logic

sortable_columns([{column: 'request_due_date', display_name: 'Request Due Date',
                   sort_with: :sort_by_due_date_nulls_first}])
# In your model, define the scope:
# scope :sort_by_due_date_nulls_first, ->(direction) {
#   order(Arel.sql("request_due_date #{direction} NULLS FIRST"))
# }

Parameters:

  • columns (Array)

    the array of hash for column and display name

Options Hash (columns):

  • :column (String)

    the database column name

  • :display_name (String)

    the display name for the sort option

  • :default (Boolean)

    whether this is the default sort column

  • :default_direction (String) — default: 'asc'

    default sort direction (‘asc’ or ‘desc’)

  • :sort_datatype (String)

    the SQL datatype for JSONB columns (e.g., ‘TIMESTAMP’, ‘INTEGER’)

  • :sort_with (Symbol)

    custom scope method name on the model for custom sorting logic

  • :display_if (Proc)

    condition to display this sort option

Raises:

  • (ArgumentError)


570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
# File 'lib/cm_admin/models/dsl_method.rb', line 570

def sortable_columns(columns)
  @sort_columns = columns.map do |column|
    column[:display_if] ||= ->(_arg) { true }
    column
  end
  default_column = @sort_columns.filter do |column|
    column.key?(:default) || column.key?(:default_direction)
  end
  raise ArgumentError, 'only one column can be default' if default_column.size > 1

  if default_column.blank?
    @default_sort_column = nil
    @default_sort_direction = 'asc'
    return
  end
  default_column = default_column.first
  @default_sort_column = default_column[:column]
  @default_sort_direction = default_column[:default_direction] if default_column[:default_direction].present?
end

#tab(tab_name, action_name, associated_model: nil, layout_type: nil, layout: nil, partial: nil, display_if: nil, show_count: true, view_type: nil, associated_model_name: nil, allow_create_action: -> { true }, &block) ⇒ Object

Create a new tab on show page.

Examples:

Creating a tab

tab :comments, 'comment', associated_model: 'comments', layout_type: 'cm_association_index', show_count: true do
  column :message
end

Parameters:

  • tab_name (String)

    or [Symbol] the name of tab

  • action_name (String)

    the name of action

  • associated_model (String) (defaults to: nil)

    the name of associated model

  • layout_type (String) (defaults to: nil)

    the layout type of tab, cm_association_index, cm_association_show

  • layout (String) (defaults to: nil)

    the layout of tab

  • partial (String) (defaults to: nil)

    the partial path of tab

  • display_if (Proc) (defaults to: nil)

    A lambda that takes the current object and return true or false

  • view_type (Symbol) (defaults to: nil)

    view type of page :table or :card

  • show_count (Boolean) (defaults to: true)

    show count of records in tab, default is true

  • associated_model_name (String) (defaults to: nil)

    the name of associated model, if not provided, it will be the child_records class name



165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/cm_admin/models/dsl_method.rb', line 165

def tab(tab_name, action_name, associated_model: nil, layout_type: nil, layout: nil, partial: nil,
        display_if: nil, show_count: true, view_type: nil, associated_model_name: nil,
        allow_create_action: -> { true }, &block)
  action_name = action_name.to_s.gsub(' ', '').underscore
  if action_name.to_s == ''
    @current_action = CmAdmin::Models::Action.find_by(self, name: 'show')
    @available_tabs << CmAdmin::Models::Tab.new(tab_name, '', display_if, show_count, &block)
  else
    action = CmAdmin::Models::Action.new(name: action_name.to_s, verb: :get, path: ":id/#{action_name}",
                                         layout_type:, layout:, partial:, child_records: associated_model,
                                         action_type: :tab, display_type: :page, model_name: name, view_type:,
                                         associated_model_name:, allow_create_action:)
    @available_actions << action
    @current_action = action
    @available_tabs << CmAdmin::Models::Tab.new(tab_name, action_name, display_if, show_count, &block)
  end
  yield if block
end