cm-admin 1.5.39 → 1.5.41

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fefeb86ee44a447393ca13e95d50acef37ecad2512b85518b24aaf09d307b777
4
- data.tar.gz: 993f0514720d1af46c50f43ce7f2a00086b1823b0621b726e0f3c400170c30ab
3
+ metadata.gz: c64d865b82c2abfd0975930a349a1ba2f63ee85d5ecbf0a4e627d3cbc1629bb1
4
+ data.tar.gz: 5ce41edd98628d3b20315a5ca44c5be95daf578bbdd53c97192f114f58110545
5
5
  SHA512:
6
- metadata.gz: 602dbdb98de4efd9c28149c8b3bb501d41274d36c13aa8053cb5e3c2e1e2b9581bfeeeb35d1650eebd81787c164cf821df146e5c030eb2ba882246dbc2d5e033
7
- data.tar.gz: 7c90ad69eafc91876869dbbe5064d59fd698435d49bcdca6fca5fc43049055b0a0786dd0a563ba05e8cf09551b17114c2f5bf9195833b6dd90aab62d983294c5
6
+ metadata.gz: 7327d73ee5028535163d62e4f3dbc7197389132178331c9bf76e4e0bb463166a06fb74186c4c32c0a50871c37ead0895e7f4b43157646bb3a5ff59b3e5e7e207
7
+ data.tar.gz: fefcc76ae24e4cc066c9dc70831fa5f1974bfe03563c49863e319180adec9577eb0dcb6acae9a4d35d4b587a7a2b602d3a09e0cab4c4b03ae8eac1e12eddd286
@@ -0,0 +1,45 @@
1
+ name: Deploy Yard Docs
2
+
3
+ on:
4
+ workflow_dispatch:
5
+
6
+ jobs:
7
+ build:
8
+ runs-on: ubuntu-latest
9
+ steps:
10
+ - uses: actions/checkout@v4
11
+ - name: Set up Ruby
12
+ uses: ruby/setup-ruby@v1
13
+ with:
14
+ ruby-version: "3.3"
15
+ - name: Install Yard gem
16
+ run: gem install yard
17
+ - name: Build Yard Docs
18
+ run: yardoc lib/cm_admin/models/dsl_method.rb
19
+ - name: Upload artifact
20
+ uses: actions/upload-pages-artifact@v1
21
+ with:
22
+ name: github-pages # name of the artifact
23
+ path: ./doc
24
+ if-no-files-found: error
25
+
26
+ deploy:
27
+ # Add a dependency to the build job
28
+ needs: build
29
+
30
+ # Grant GITHUB_TOKEN the permissions required to make a Pages deployment
31
+ permissions:
32
+ pages: write # to deploy to Pages
33
+ id-token: write # to verify the deployment originates from an appropriate source
34
+
35
+ # Deploy to the github-pages environment
36
+ environment:
37
+ name: github-pages # artifact name
38
+ url: ${{ steps.deployment.outputs.page_url }}
39
+
40
+ # Specify runner + deployment step
41
+ runs-on: ubuntu-latest
42
+ steps:
43
+ - name: Deploy to GitHub Pages
44
+ id: deployment
45
+ uses: actions/deploy-pages@v2
data/CNAME ADDED
@@ -0,0 +1 @@
1
+ docs.cm-admin.commutatus.com
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cm-admin (1.5.39)
4
+ cm-admin (1.5.41)
5
5
  caxlsx_rails
6
6
  cocoon (~> 1.2.15)
7
7
  csv-importer (~> 0.8.2)
data/README.md CHANGED
@@ -37,13 +37,19 @@ For demo repo check [here](https://github.com/commutatus/cm-admin-panel-demo)
37
37
 
38
38
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
39
39
 
40
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
40
+ To install this gem onto your local machine, run `bundle exec rake install`.
41
+
42
+ ## Deployment
43
+
44
+ To release a new version of the Gem you can use Github Actions.
45
+
46
+ Go to Actions tab in your repository and click on `Bump Gem` workflow.
47
+ You will see `Run workflow` button click on it and choose `Bump Type`, then click `Run Workflow` and it will bump the version of the gem and push the changes to the repository.
41
48
 
42
49
  ## Contributing
43
50
 
44
51
  Bug reports and pull requests are welcome on [GitHub](https://github.com/commutatus/cm-admin).
45
52
 
46
-
47
53
  ## License
48
54
 
49
55
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -1,12 +1,12 @@
1
- import './scaffolds.js';
2
- import './shared_scaffolds.js';
3
-
4
- import './bulk_actions.js'
5
- import './cocoon.js'
6
- import './exports.js'
7
- import './filters.js'
8
- import './form_validation.js'
9
- import './quick_search.js'
10
- import './custom.js'
11
- import './kanban.js'
1
+ import "./scaffolds.js";
2
+ import "./shared_scaffolds.js";
12
3
 
4
+ import "./bulk_actions.js";
5
+ import "./cocoon.js";
6
+ import "./exports.js";
7
+ import "./filters.js";
8
+ import "./form_validation.js";
9
+ import "./quick_search.js";
10
+ import "./custom.js";
11
+ import "./kanban.js";
12
+ import "./custom_action";
@@ -0,0 +1,30 @@
1
+ import * as bootstrap from "bootstrap";
2
+ window.bootstrap = bootstrap;
3
+
4
+ document.addEventListener("turbo:load", function () {
5
+ $('[data-action="fetch-modal"]').on("click", function (e) {
6
+ const actionName = $(this).attr("data-action_name");
7
+ const modelName = $(this).attr("data-model_name");
8
+ const recordId = $(this).attr("data-record_id");
9
+ const modalContainer = $(
10
+ "[data-behaviour='custom-action-modal-container']"
11
+ );
12
+ const routeMount = document.location.href.split("/")[3];
13
+
14
+ if (!actionName || !modelName || !recordId || !modalContainer) return;
15
+ $.ajax({
16
+ url: `/${routeMount}/${modelName}/${recordId}/custom_action_modal/${actionName}`,
17
+ method: "GET",
18
+ success: function (response) {
19
+ modalContainer.html(response);
20
+ const actionModal = new bootstrap.Modal(
21
+ modalContainer.children().first()
22
+ );
23
+ actionModal.show();
24
+ },
25
+ error: function (error) {
26
+ console.error("Error:", error);
27
+ },
28
+ });
29
+ });
30
+ });
@@ -109,6 +109,18 @@
109
109
  }
110
110
 
111
111
  //checkbox-style
112
+
113
+ .cm-checkbox-section {
114
+ width: 100%;
115
+ float: left;
116
+ .cm-checkbox-label {
117
+ float: left;
118
+ padding-left: 10px;
119
+ }
120
+ .cm-checkbox-tag {
121
+ float: left;
122
+ }
123
+ }
112
124
  input.cm-checkbox[type="checkbox"] {
113
125
  position: relative;
114
126
  // display: block;
@@ -89,7 +89,7 @@ module CmAdmin
89
89
  end
90
90
 
91
91
  def import
92
- @model = Model.find_by({name: controller_name.titleize})
92
+ @model = Model.find_by({name: controller_name.classify})
93
93
  allowed_params = params.permit(file_import: [:associated_model_name, :import_file]).to_h
94
94
  file_import = ::FileImport.new(allowed_params[:file_import])
95
95
  file_import.added_by = Current.user
@@ -101,7 +101,7 @@ module CmAdmin
101
101
  end
102
102
 
103
103
  def import_form
104
- @model = Model.find_by({name: controller_name.titleize})
104
+ @model = Model.find_by({name: controller_name.classify})
105
105
  respond_to do |format|
106
106
  format.html { render '/cm_admin/main/import_form' }
107
107
  end
@@ -172,6 +172,17 @@ module CmAdmin
172
172
  end
173
173
  end
174
174
 
175
+ def cm_custom_action_modal(params)
176
+ scoped_model = "CmAdmin::#{@model.name}Policy::Scope".constantize.new(Current.user, @model.name.constantize).resolve
177
+ @ar_object = fetch_ar_object(scoped_model, params[:id])
178
+ if params[:action_name] == 'destroy'
179
+ render partial: '/layouts/destroy_action_modal', locals: { ar_object: @ar_object }
180
+ else
181
+ custom_action = @model.available_actions.select { |x| x.name == params[:action_name].to_s }.first
182
+ render partial: '/layouts/custom_action_modal', locals: { custom_action:, ar_object: @ar_object }
183
+ end
184
+ end
185
+
175
186
  def get_nested_table_fields(fields)
176
187
  nested_table_fields = []
177
188
  fields.each do |field|
@@ -1,30 +1,27 @@
1
- require("@rails/ujs").start()
2
- require("turbolinks").start()
3
- require("@rails/activestorage").start()
4
- require("stylesheets/cm_admin/application")
5
- require("jquery")
6
- require("moment")
7
- require("bootstrap")
8
- require('flatpickr')
9
- require("jgrowl")
10
- require("trix")
11
- require('./scaffolds.js')
12
- require('/app/assets/javascripts/cm_admin/shared_scaffolds.js')
13
- require('/app/assets/javascripts/cm_admin/form_validation.js')
14
- require('/app/assets/javascripts/cm_admin/quick_search.js')
15
- require('/app/assets/javascripts/cm_admin/filters.js')
16
- require('/app/assets/javascripts/cm_admin/exports.js')
17
- require('/app/assets/javascripts/cm_admin/bulk_actions.js')
18
-
19
- import jQuery from 'jquery'
20
- import LocalTime from "local-time"
21
- window.$ = jQuery
22
- window.jQuery = jQuery
23
-
24
- LocalTime.start()
25
- require("@nathanvda/cocoon")
26
- import "@fortawesome/fontawesome-free/css/all"
27
- import "daterangepicker"
28
-
1
+ require("@rails/ujs").start();
2
+ require("turbolinks").start();
3
+ require("@rails/activestorage").start();
4
+ require("stylesheets/cm_admin/application");
5
+ require("jquery");
6
+ require("moment");
7
+ require("bootstrap");
8
+ require("flatpickr");
9
+ require("jgrowl");
10
+ require("trix");
11
+ require("./scaffolds.js");
12
+ require("/app/assets/javascripts/cm_admin/shared_scaffolds.js");
13
+ require("/app/assets/javascripts/cm_admin/form_validation.js");
14
+ require("/app/assets/javascripts/cm_admin/quick_search.js");
15
+ require("/app/assets/javascripts/cm_admin/filters.js");
16
+ require("/app/assets/javascripts/cm_admin/exports.js");
17
+ require("/app/assets/javascripts/cm_admin/bulk_actions.js");
29
18
 
19
+ import jQuery from "jquery";
20
+ import LocalTime from "local-time";
21
+ window.$ = jQuery;
22
+ window.jQuery = jQuery;
30
23
 
24
+ LocalTime.start();
25
+ require("@nathanvda/cocoon");
26
+ import "@fortawesome/fontawesome-free/css/all";
27
+ import "daterangepicker";
@@ -27,7 +27,7 @@
27
27
  i.fa.fa-trash
28
28
  | Destroy
29
29
  - else
30
- = link_to '', data: { bs_toggle: 'modal', bs_target: "##{@model.name.classify}DestroyModal-#{ar_object.id.to_s}" } do
30
+ div data={action: 'fetch-modal', model_name: "#{cm_model.name.underscore.pluralize}", action_name: "destroy", record_id: "#{ar_object.id.to_s}"}
31
31
  .popup-option
32
32
  span
33
33
  i.fa.fa-trash
@@ -42,7 +42,7 @@
42
42
  i class="#{custom_action.icon_name}"
43
43
  = custom_action_title(custom_action)
44
44
  - when :modal
45
- = link_to '', data: { bs_toggle: 'modal', bs_target: "##{custom_action.name.classify}Modal-#{ar_object.id.to_s}" } do
45
+ div data={action: 'fetch-modal', model_name: "#{cm_model.name.underscore.pluralize}", action_name: "#{custom_action.name}", record_id: "#{ar_object.id.to_s}"}
46
46
  .popup-option
47
47
  span
48
48
  i class="#{custom_action.icon_name}"
@@ -8,4 +8,4 @@
8
8
  - if custom_action.partial
9
9
  = render partial: custom_action.partial, locals: { custom_action: custom_action, ar_object: ar_object }
10
10
  - else
11
- = render partial: 'cm_admin/main/custom_action_modal_form', locals: { custom_action: custom_action, ar_object: ar_object }
11
+ = render partial: 'cm_admin/main/custom_action_modal_form', locals: { custom_action: custom_action, ar_object: ar_object }
@@ -1,24 +1,25 @@
1
1
  - custom_action_with_modals = @model.available_actions.select{ |act| act if act.display_type == :modal && act.action_type == :custom }
2
2
  - bulk_action_with_modals = @model.available_actions.select{ |act| act if act.display_type == :modal && act.action_type == :bulk_action }
3
3
  - destroy_action = @ar_object ? available_actions(@model, @ar_object, 'destroy') : nil
4
+
5
+ div[data-behaviour="custom-action-modal-container"]
6
+
4
7
  - if @associated_model
5
8
  - custom_action_with_modals += @associated_model.available_actions.select{ |act| act if act.display_type == :modal }
9
+
6
10
  - if @current_action&.name == 'index'
7
11
  - bulk_action_with_modals.each do |bulk_action|
8
12
  = render partial: '/layouts/custom_action_modal', locals: { custom_action: bulk_action, ar_object: nil }
9
- - @ar_object.data.each do |ar_object|
10
- - custom_action_with_modals.each do |custom_action|
11
- = render partial: '/layouts/custom_action_modal', locals: { custom_action: custom_action, ar_object: ar_object }
12
- - if destroy_action
13
- = render partial: '/layouts/destroy_action_modal', locals: { ar_object: ar_object }
13
+
14
14
  - elsif @current_action&.name == 'show'
15
15
  - custom_action_with_modals.each do |custom_action|
16
- = render partial: '/layouts/custom_action_modal', locals: { custom_action: custom_action, ar_object: @ar_object }
16
+ = render partial: '/layouts/custom_action_modal', locals: { custom_action: custom_action, ar_object: @ar_object }
17
17
  - if destroy_action
18
18
  = render partial: '/layouts/destroy_action_modal', locals: { ar_object: @ar_object }
19
+
19
20
  - elsif @current_action&.action_type == :custom && @associated_ar_object&.data.present?
20
21
  - @associated_ar_object.data.each do |ar_object|
21
22
  - custom_action_with_modals.select{|custom_action| custom_action.model_name == @current_action.child_records.to_s.classify}.each do |custom_action|
22
23
  = render partial: '/layouts/custom_action_modal', locals: { custom_action: custom_action, ar_object: ar_object }
23
24
  - if destroy_action
24
- = render partial: '/layouts/destroy_action_modal', locals: { ar_object: ar_object }
25
+ = render partial: '/layouts/destroy_action_modal', locals: { ar_object: ar_object }
data/config/routes.rb CHANGED
@@ -16,7 +16,7 @@ CmAdmin::Engine.routes.draw do
16
16
  send(:post, 'import', to: "#{model.name.underscore}#import", as: "#{model.name.underscore}_import")
17
17
  end
18
18
  end
19
-
19
+
20
20
  model.available_actions.sort_by {|act| act.name}.each do |act|
21
21
  scope model.name.tableize do
22
22
  # Define route only when action trail related field is present
@@ -34,6 +34,10 @@ module CmAdmin
34
34
  },
35
35
  custom_action: {
36
36
  verb: :post
37
+ },
38
+ custom_action_modal: {
39
+ verb: :get,
40
+ path: ':id/custom_action_modal/:action_name'
37
41
  }
38
42
  }
39
43
  REJECTABLE_FIELDS = %w(id created_at updated_at)
@@ -3,42 +3,109 @@ module CmAdmin
3
3
  module DslMethod
4
4
  extend ActiveSupport::Concern
5
5
 
6
- def cm_index(page_title: nil, page_description: nil, partial: nil, view_type: :table, &block)
6
+ # Create a table for index page with pagination.
7
+ #
8
+ # @param page_title [String] the title of page
9
+ # @param page_description [String] the description of page
10
+ # @param partial [String] the partial path of page
11
+ # @param view_type [Symbol] view type of page +:table+, +:card+ or +:kanban+
12
+ # @example Index page
13
+ # cm_index do
14
+ # page_title 'Post'
15
+ # column :title
16
+ # column :created_at, field_type: :date, format: '%d %b, %Y'
17
+ # column :updated_at, field_type: :date, format: '%d %b, %Y', header: 'Last Updated At'
18
+ # end
19
+ # rdoc-image:/public/examples/cm_index.png
20
+ def cm_index(page_title: nil, page_description: nil, partial: nil, view_type: :table)
7
21
  @current_action = CmAdmin::Models::Action.find_by(self, name: 'index')
8
22
  @current_action.set_values(page_title, page_description, partial, view_type)
9
23
  yield
10
24
  end
11
25
 
12
- def cm_show(page_title: nil, page_description: nil, partial: nil, &block)
26
+ # Create a view for show page
27
+ #
28
+ # @param page_title [String | Symbol] the title of page, if symbol passed, it will be a method name on model
29
+ # @param page_description [String] the description of page
30
+ # @param partial [String] the partial path of page
31
+ #
32
+ # @example Showing page
33
+ # cm_show page_title: :title do
34
+ # tab :profile, '' do
35
+ # cm_section 'Post Details' do
36
+ # field :title
37
+ # field :body, field_type: :rich_text
38
+ # field :is_featured
39
+ # field :status, field_type: :tag, tag_class: STATUS_TAG_COLOR
40
+ # end
41
+ # end
42
+ # end
43
+ def cm_show(page_title: nil, page_description: nil, partial: nil)
13
44
  @current_action = CmAdmin::Models::Action.find_by(self, name: 'show')
14
45
  @current_action.set_values(page_title, page_description, partial)
15
46
  yield
16
47
  end
17
48
 
18
- def cm_edit(page_title: nil, page_description: nil, partial: nil, redirect_to: nil, &block)
49
+ # Create a form for edit action
50
+ #
51
+ # @param page_title [String] or [Symbol] the title of page, if symbol passed, it will be a method name on model
52
+ # @param page_description [String] the description of page
53
+ # @param partial [String] the partial path of page
54
+ # @param redirect_to [Proc, nil] A lambda that takes the current object and redirect to path after update
55
+ #
56
+ # @example Editing page with a redirect
57
+ # cm_edit(page_title: "Edit Post", page_description: 'Enter all details to edit Post', redirect_to: ->(current_object) { "/pages/#{current_object.id}" }) do
58
+ # cm_section 'Details' do
59
+ # form_field :title, input_type: :string
60
+ # form_field :body, input_type: :rich_text
61
+ # end
62
+ # end
63
+ def cm_edit(page_title: nil, page_description: nil, partial: nil, redirect_to: nil)
19
64
  @current_action = CmAdmin::Models::Action.find_by(self, name: 'edit')
20
65
  @current_action.set_values(page_title, page_description, partial, redirect_to)
21
66
  yield
22
67
  end
23
68
 
24
- def cm_new(page_title: nil, page_description: nil, partial: nil, redirect_to: nil, &block)
69
+ # Create a form for new action
70
+ #
71
+ # @param page_title [String] the title of page
72
+ # @param page_description [String] the description of page
73
+ # @param partial [String] the partial path of page
74
+ # @param redirect_to [Proc, nil] A lambda that takes the current object and redirect to path after create
75
+ #
76
+ # @example Creating a new page with a redirect
77
+ # cm_new(page_title: "Add Post", page_description: 'Enter all details to add Post', redirect_to: ->(current_object) { "/pages/#{current_object.id}" }) do
78
+ # cm_section 'Details' do
79
+ # form_field :title, input_type: :string
80
+ # form_field :body, input_type: :rich_text
81
+ # end
82
+ # end
83
+ def cm_new(page_title: nil, page_description: nil, partial: nil, redirect_to: nil)
25
84
  @current_action = CmAdmin::Models::Action.find_by(self, name: 'new')
26
85
  @current_action.set_values(page_title, page_description, partial, redirect_to)
27
86
  yield
28
87
  end
29
88
 
89
+ # Set page title for current action
90
+ # @param title [String] the title of page
30
91
  def page_title(title)
31
92
  return unless @current_action
32
93
 
33
94
  @current_action.page_title = title
34
95
  end
35
96
 
97
+ # Set page description for current action
98
+ # @param description [String] the description of page
36
99
  def page_description(description)
37
100
  return unless @current_action
38
101
 
39
102
  @current_action.page_description = description
40
103
  end
41
104
 
105
+ # Set kanban view for current action
106
+ # @param column_name [String] the name of column
107
+ # @param exclude [Array] the array of fields to exclude
108
+ # @param only [Array] the array of fields to include
42
109
  def kanban_view(column_name, exclude: [], only: [])
43
110
  return unless @current_action
44
111
 
@@ -47,19 +114,35 @@ module CmAdmin
47
114
  @current_action.kanban_attr[:only] = only
48
115
  end
49
116
 
117
+ # Set scopes for current action
118
+ # @param scopes [Array] the array of scopes
50
119
  def scope_list(scopes = [])
51
120
  return unless @current_action
52
121
 
53
122
  @current_action.scopes = scopes
54
123
  end
55
124
 
125
+ # Create a new tab on show page.
126
+ #
127
+ # @param tab_name [String] or [Symbol] the name of tab
128
+ # @param custom_action [String] the name of custom action
129
+ # @param associated_model [String] the name of associated model
130
+ # @param layout_type [String] the layout type of tab, +cm_association_index+, +cm_association_show+
131
+ # @param layout [String] the layout of tab
132
+ # @param partial [String] the partial path of tab
133
+ # @param display_if [Proc] A lambda that takes the current object and return true or false
134
+ #
135
+ # @example Creating a tab
136
+ # tab :comments, 'comment', associated_model: 'comments', layout_type: 'cm_association_index' do
137
+ # column :message
138
+ # end
56
139
  def tab(tab_name, custom_action, associated_model: nil, layout_type: nil, layout: nil, partial: nil, display_if: nil, &block)
57
140
  if custom_action.to_s == ''
58
141
  @current_action = CmAdmin::Models::Action.find_by(self, name: 'show')
59
142
  @available_tabs << CmAdmin::Models::Tab.new(tab_name, '', display_if, &block)
60
143
  else
61
144
  action = CmAdmin::Models::Action.new(name: custom_action.to_s, verb: :get, path: ':id/' + custom_action,
62
- layout_type: layout_type, layout: layout, partial: partial, child_records: associated_model,
145
+ layout_type:, layout:, partial:, child_records: associated_model,
63
146
  action_type: :custom, display_type: :page, model_name: name)
64
147
  @available_actions << action
65
148
  @current_action = action
@@ -68,21 +151,53 @@ module CmAdmin
68
151
  yield if block
69
152
  end
70
153
 
154
+ # Create a new row on page or form.
155
+ # @param display_if [Proc] A lambda that takes the current object and return true or false
156
+ # @param html_attrs [Hash] A hash that contains html attributes
157
+ # @example Creating a row
158
+ # row(display_if: ->(current_object) { current_object.name == 'John' }, html_attrs: { class: 'row-class' }) do
159
+ # cm_section 'Details' do
160
+ # form_field :title, input_type: :string
161
+ # end
162
+ # end
71
163
  def row(display_if: nil, html_attrs: nil, &block)
72
164
  @available_fields[@current_action.name.to_sym] ||= []
73
165
  @available_fields[@current_action.name.to_sym] << CmAdmin::Models::Row.new(@current_action, @model, display_if, html_attrs, &block)
74
166
  end
75
167
 
168
+ # Create a new section on page or form.
169
+ # @param section_name [String] the name of section
170
+ # @param display_if [Proc] A lambda that takes the current object and return true or false
171
+ # @param col_size [Integer] the size of column according to grid view
172
+ # @param html_attrs [Hash] A hash that contains html attributes
173
+
174
+ # @example Creating a section
175
+ # cm_section('Basic Information', display_if: ->(current_object) { current_object.name == 'John' }, col_size: 6, html_attrs: { class: 'section-class' }) do
176
+ # field :title, input_type: :string
177
+ # end
76
178
  def cm_section(section_name, display_if: nil, col_size: nil, html_attrs: nil, &block)
77
179
  @available_fields[@current_action.name.to_sym] ||= []
78
180
  @available_fields[@current_action.name.to_sym] << CmAdmin::Models::Section.new(section_name, @current_action, @model, display_if, html_attrs, col_size, &block)
79
181
  end
80
182
 
81
- # This method is deprecated. Use cm_section instead.
183
+ # @deprecated Use {#cm_section} instead of this method
82
184
  def cm_show_section(section_name, display_if: nil, html_attrs: nil, &block)
83
- cm_section(section_name, display_if: display_if, html_attrs: html_attrs, &block)
185
+ cm_section(section_name, display_if:, html_attrs:, &block)
84
186
  end
85
187
 
188
+ # Create a new column on index layout.
189
+ # @param field_name [String] the name of field
190
+ # @param 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+
191
+ # @param header [String] the header of field
192
+ # @param format [String] the format of field for date field
193
+ # @param helper_method [Symbol] the helper method for field, should be defined in custom_helper.rb file, will take two arguments, +record+ and +field_name+
194
+ # @param height [Integer] the height of field for image field
195
+ # @param width [Integer] the width of field for image field
196
+ # @params custom_link [String] the custom link for field
197
+ # @params tag_class [Hash] the tag class for field, example: { approved: 'completed', draft: 'active-two', rejected: 'danger' }, this will add css class to tag
198
+ # @params display_if [Proc] A lambda that takes the current object and return true or false
199
+ # @example Creating a column
200
+ # column('name', field_type: :string)
86
201
  def column(field_name, options = {})
87
202
  @available_fields[@current_action.name.to_sym] ||= []
88
203
  raise 'Only one column can be locked in a table.' if @available_fields[@current_action.name.to_sym].select { |x| x.lockable }.size > 0 && options[:lockable]
@@ -105,6 +220,10 @@ module CmAdmin
105
220
  @available_fields[@current_action.name.to_sym] << CmAdmin::Models::Column.new(field_name, options)
106
221
  end
107
222
 
223
+ # Get all columns for a model for index layout.
224
+ # @param exclude [Array] the array of fields to exclude
225
+ # @example Getting all columns
226
+ # all_db_columns(exclude: ['id'])
108
227
  def all_db_columns(options = {})
109
228
  field_names = instance_variable_get(:@ar_model)&.columns&.map { |x| x.name.to_sym }
110
229
  if options.include?(:exclude) && field_names
@@ -116,49 +235,103 @@ module CmAdmin
116
235
  end
117
236
  end
118
237
 
119
- # Custom actions
120
- # eg
121
- # class User < ApplicationRecord
122
- # cm_admin do
123
- # custom_action name: 'submit', verb: 'post', path: ':id/submit' do
124
- # def user_submit
125
- # Code for action here...
126
- # end
127
- # end
238
+ # Create a new custom action for model
239
+ # @param name [String] the name of action
240
+ # @param page_title [String] the title of page
241
+ # @param page_description [String] the description of page
242
+ # @param display_name [String] the display name of action
243
+ # @param verb [String] the verb of action, +get+, +post+, +put+, +patch+ or +delete+
244
+ # @param layout [String] the layout of action
245
+ # @param layout_type [String] the layout type of action, +cm_association_index+, +cm_association_show+
246
+ # @param partial [String] the partial path of action
247
+ # @param path [String] the path of action
248
+ # @param display_type [Symbol] the display type of action, +:button+, +:modal+
249
+ # @param modal_configuration [Hash] the configuration of modal
250
+ # @param url_params [Hash] the url params of action
251
+ # @param display_if [Proc] A lambda that takes the current object and return true or false
252
+ # @param route_type [String] the route type of action, +member+, +collection+
253
+ # @param icon_name [String] the icon name of action, follow font-awesome icon name
254
+ # @example Creating a custom action with modal
255
+ # 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
256
+ # post = ::Post.find(params[:id])
257
+ # post.approved!
258
+ # post
259
+ # end
260
+ # @example Creating a custom action with button
261
+ # 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?) do
262
+ # post = ::Post.find(params[:id])
263
+ # post.approved!
264
+ # post
128
265
  # end
129
- # end
130
- 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, modal_configuration: {}, url_params: {}, display_if: ->(arg) { true }, route_type: nil, icon_name: 'fa fa-th-large', &block)
266
+ 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, modal_configuration: {}, url_params: {}, display_if: ->(_arg) { true }, route_type: nil, icon_name: 'fa fa-th-large', &block)
131
267
  action = CmAdmin::Models::CustomAction.new(
132
- page_title: page_title, page_description: page_description,
133
- name: name, display_name: display_name, verb: verb, layout: layout,
134
- layout_type: layout_type, partial: partial, path: path,
135
- parent: current_action.name, display_type: display_type, display_if: display_if,
136
- action_type: :custom, route_type: route_type, icon_name: icon_name, modal_configuration: modal_configuration,
137
- model_name: self.name, url_params: url_params, &block
268
+ page_title:, page_description:,
269
+ name:, display_name:, verb:, layout:,
270
+ layout_type:, partial:, path:,
271
+ parent: current_action.name, display_type:, display_if:,
272
+ action_type: :custom, route_type:, icon_name:, modal_configuration:,
273
+ model_name: self.name, url_params:, &block
138
274
  )
139
275
  @available_actions << action
140
- # self.class.class_eval(&block)
141
276
  end
142
277
 
143
- def bulk_action(name: nil, display_name: nil, display_if: ->(arg) { true }, redirection_url: nil, icon_name: nil, verb: nil, display_type: nil, modal_configuration: {}, route_type: nil, partial: nil, &block)
278
+ # Create a new bulk action for model
279
+ # @param name [String] the name of action
280
+ # @param display_name [String] the display name of action
281
+ # @param display_if [Proc] A lambda that takes the current object and return true or false
282
+ # @param redirection_url [String] the redirection url of action
283
+ # @param icon_name [String] the icon name of action, follow font-awesome icon name
284
+ # @param verb [String] the verb of action, +get+, +post+, +put+, +patch+ or +delete+
285
+ # @param display_type [Symbol] the display type of action, +:page+, +:modal+
286
+ # @param modal_configuration [Hash] the configuration of modal
287
+ # @param route_type [String] the route type of action, +member+, +collection+
288
+ # @param partial [String] the partial path of action
289
+ # @example Creating a bulk action
290
+ # 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' } do
291
+ # posts = ::Post.where(id: params[:ids])
292
+ # posts.each(&:approved!)
293
+ # posts
294
+ # end
295
+ def bulk_action(name: nil, display_name: nil, display_if: ->(_arg) { true }, redirection_url: nil, icon_name: nil, verb: nil, display_type: nil, modal_configuration: {}, route_type: nil, partial: nil, &block)
144
296
  bulk_action = CmAdmin::Models::BulkAction.new(
145
- name: name, display_name: display_name, display_if: display_if, modal_configuration: modal_configuration,
146
- redirection_url: redirection_url, icon_name: icon_name, action_type: :bulk_action,
147
- verb: verb, display_type: display_type, route_type: route_type, partial: partial, &block
297
+ name:, display_name:, display_if:, modal_configuration:,
298
+ redirection_url:, icon_name:, action_type: :bulk_action,
299
+ verb:, display_type:, route_type:, partial:, &block
148
300
  )
149
301
  @available_actions << bulk_action
150
302
  end
151
303
 
304
+ # Create a new filter for model
305
+ # @param db_column_name [String] the name of column
306
+ # @param filter_type [String] the type of filter, +:date+, +:multi_select+, +:range+, +:search+, +:single_select+
307
+ # @param placeholder [String] the placeholder of filter
308
+ # @param helper_method [String] the helper method for filter, should be defined in custom_helper.rb file
309
+ # @param filter_with [Symbol] filter with scope name on model
310
+ # @param collection [Array] the collection of filter, use with single_select or multi_select
311
+ # @example Creating a filter
312
+ # filter('name', :search)
313
+ # filter('created_at', :date)
314
+ # filter('status', :single_select, collection: ['draft', 'published'])
315
+ # filter('status', :multi_select, helper_method: 'status_collection')
316
+ # filter('age', :range)
152
317
  def filter(db_column_name, filter_type, options = {})
153
- @filters << CmAdmin::Models::Filter.new(db_column_name: db_column_name, filter_type: filter_type, options: options)
318
+ @filters << CmAdmin::Models::Filter.new(db_column_name:, filter_type:, options:)
154
319
  end
155
320
 
321
+ # Set sort direction for filters
322
+ # @param direction [Symbol] the direction of sort, +:asc+, +:desc+
323
+ # @example Setting sort direction
324
+ # sort_direction(:asc)
156
325
  def sort_direction(direction = :desc)
157
326
  raise ArgumentError, "Select a valid sort direction like #{CmAdmin::Models::Action::VALID_SORT_DIRECTION.join(' or ')} instead of #{direction}" unless CmAdmin::Models::Action::VALID_SORT_DIRECTION.include?(direction.to_sym.downcase)
158
327
 
159
328
  @current_action.sort_direction = direction.to_sym if @current_action
160
329
  end
161
330
 
331
+ # Set sort column for filters
332
+ # @param column [Symbol] the column name
333
+ # @example Setting sort column
334
+ # sort_column(:created_at)
162
335
  def sort_column(column = :created_at)
163
336
  @current_action.sort_column = column.to_sym if @current_action
164
337
  end
@@ -43,6 +43,12 @@ module CmAdmin
43
43
  def cm_show_section(section_name, col_size: nil, display_if: nil, html_attrs: nil, &block)
44
44
  cm_section(section_name, col_size: col_size, display_if: display_if, html_attrs: html_attrs, &block)
45
45
  end
46
+
47
+ def nested_form_section(section_name, display_if: nil, col_size: nil, html_attrs: nil, &block)
48
+ nested_section = CmAdmin::Models::Section.new(section_name, @current_action, @cm_model, display_if, html_attrs, col_size, &block)
49
+ nested_section.parent_section = self
50
+ @row_fields << nested_section
51
+ end
46
52
  end
47
53
  end
48
54
  end
@@ -1,3 +1,3 @@
1
1
  module CmAdmin
2
- VERSION = '1.5.39'
2
+ VERSION = '1.5.41'
3
3
  end
@@ -87,6 +87,7 @@ module CmAdmin
87
87
  ar_object.send(field.field_name).to_s.titleize
88
88
  when :tag
89
89
  tag_class = field.tag_class.dig("#{ar_object.send(field.field_name.to_s)}".to_sym).to_s
90
+ tag_class = 'neutral' if tag_class.blank?
90
91
  content_tag :span, class: "status-tag #{tag_class}" do
91
92
  ar_object.send(field.field_name).to_s.titleize.upcase
92
93
  end
@@ -56,7 +56,7 @@ module CmAdmin
56
56
 
57
57
  def cm_switch_field(form_obj, cm_field, value, required_class, _target_action, _ajax_url)\
58
58
  content_tag :div, class: 'form-check form-switch' do
59
- form_obj.check_box cm_field.field_name,
59
+ concat form_obj.check_box cm_field.field_name,
60
60
  merge_wrapper_options(
61
61
  {
62
62
  class: "field-control form-check-input #{required_class}",
@@ -65,6 +65,7 @@ module CmAdmin
65
65
  role: 'switch'
66
66
  }, cm_field.html_attrs
67
67
  )
68
+ concat content_tag(:div, cm_field.field_name.to_s.titleize, class: 'cm-switch-label')
68
69
  end
69
70
  end
70
71
 
@@ -272,18 +273,10 @@ module CmAdmin
272
273
  if value.instance_of?(Array)
273
274
  format_check_box_array(value, form_obj, cm_field, required_class, target_action)
274
275
  else
275
- form_obj.check_box cm_field.field_name,
276
- merge_wrapper_options(
277
- {
278
- class: "cm-checkbox #{required_class} #{target_action.present? ? 'linked-field-request' : ''}",
279
- disabled: cm_field.disabled.call(form_obj.object),
280
- data: {
281
- field_name: cm_field.field_name,
282
- target_action: target_action&.name,
283
- target_url: target_action&.name ? cm_admin.send("#{@model.name.underscore}_#{target_action&.name}_path") : ''
284
- }
285
- }, cm_field.html_attrs
286
- )
276
+ content_tag :div, class: 'cm-checkbox-section' do
277
+ concat single_checkbox_tag(value, form_obj, cm_field, required_class, target_action)
278
+ concat content_tag(:div, cm_field.field_name.to_s.titleize, class: 'cm-checkbox-label')
279
+ end
287
280
  end
288
281
  end
289
282
 
@@ -302,8 +295,25 @@ module CmAdmin
302
295
  end
303
296
  end
304
297
 
298
+ def single_checkbox_tag(value, form_obj, cm_field, required_class, target_action)
299
+ content_tag :div, class: 'cm-checkbox-tag' do
300
+ concat form_obj.check_box cm_field.field_name,
301
+ merge_wrapper_options(
302
+ {
303
+ class: "cm-checkbox #{required_class} #{target_action.present? ? 'linked-field-request' : ''}",
304
+ disabled: cm_field.disabled.call(form_obj.object),
305
+ data: {
306
+ field_name: cm_field.field_name,
307
+ target_action: target_action&.name,
308
+ target_url: target_action&.name ? cm_admin.send("#{@model.name.underscore}_#{target_action&.name}_path") : ''
309
+ }
310
+ }, cm_field.html_attrs
311
+ )
312
+ end
313
+ end
314
+
305
315
  def format_check_box_tag(val, form_obj, cm_field, required_class, target_action)
306
- content_tag :div, class: 'cm-radio-tag' do
316
+ content_tag :div, class: 'cm-checkbox-tag' do
307
317
  if val.present?
308
318
  concat form_obj.text_field cm_field.field_name, name: "#{@model.name.underscore}[#{cm_field.field_name}][]", value: '0', hidden: true, disabled: 'disabled'
309
319
  else
@@ -74,7 +74,11 @@ module CmAdmin
74
74
  rows.each do |row|
75
75
  concat(content_tag(:div, class: 'row') do
76
76
  row.row_fields.each do |field|
77
- concat set_form_field(resource, form_obj, field)
77
+ if field.is_a?(CmAdmin::Models::Section)
78
+ concat set_nested_section_form_fields(resource, form_obj, Array(field))
79
+ else
80
+ concat set_form_field(resource, form_obj, field)
81
+ end
78
82
  end
79
83
  end)
80
84
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cm-admin
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.39
4
+ version: 1.5.41
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael
@@ -14,7 +14,7 @@ authors:
14
14
  autorequire:
15
15
  bindir: exe
16
16
  cert_chain: []
17
- date: 2024-07-24 00:00:00.000000000 Z
17
+ date: 2024-08-01 00:00:00.000000000 Z
18
18
  dependencies:
19
19
  - !ruby/object:Gem::Dependency
20
20
  name: caxlsx_rails
@@ -175,6 +175,7 @@ files:
175
175
  - ".github/ISSUE_TEMPLATE/bug_report.md"
176
176
  - ".github/ISSUE_TEMPLATE/config.yml"
177
177
  - ".github/ISSUE_TEMPLATE/feature_request.md"
178
+ - ".github/workflows/deploy-yard-docs.yml"
178
179
  - ".github/workflows/linters.yml"
179
180
  - ".github/workflows/release-gem.yml"
180
181
  - ".gitignore"
@@ -184,6 +185,7 @@ files:
184
185
  - ".stylelintrc.json"
185
186
  - ".travis.yml"
186
187
  - ".vscode/settings.json"
188
+ - CNAME
187
189
  - CODE_OF_CONDUCT.md
188
190
  - Gemfile
189
191
  - Gemfile.lock
@@ -205,6 +207,7 @@ files:
205
207
  - app/assets/javascripts/cm_admin/bulk_actions.js
206
208
  - app/assets/javascripts/cm_admin/cocoon.js
207
209
  - app/assets/javascripts/cm_admin/custom.js
210
+ - app/assets/javascripts/cm_admin/custom_action.js
208
211
  - app/assets/javascripts/cm_admin/exports.js
209
212
  - app/assets/javascripts/cm_admin/filters.js
210
213
  - app/assets/javascripts/cm_admin/form_validation.js