cm-admin 0.2.0 → 0.4.2

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.
Files changed (132) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +12 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +6 -0
  5. data/Gemfile +7 -1
  6. data/Gemfile.lock +121 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +8 -19
  9. data/app/assets/images/logo.png +0 -0
  10. data/{lib/generators/cm_admin/templates/assets/stylesheets → app/assets/stylesheets/cm_admin}/base/auth.scss +4 -6
  11. data/app/assets/stylesheets/cm_admin/base/common.scss +237 -0
  12. data/app/assets/stylesheets/cm_admin/base/filters.scss +200 -0
  13. data/{lib/generators/cm_admin/templates/assets/stylesheets → app/assets/stylesheets/cm_admin}/base/form.scss +83 -11
  14. data/app/assets/stylesheets/cm_admin/base/main-nav.scss +47 -0
  15. data/app/assets/stylesheets/cm_admin/base/navbar.scss +65 -0
  16. data/app/assets/stylesheets/cm_admin/base/quicksearch.scss +76 -0
  17. data/app/assets/stylesheets/cm_admin/base/scaffold.scss +96 -0
  18. data/app/assets/stylesheets/cm_admin/base/show.scss +76 -0
  19. data/app/assets/stylesheets/cm_admin/base/sidebar.scss +225 -0
  20. data/app/assets/stylesheets/cm_admin/base/table.scss +320 -0
  21. data/app/assets/stylesheets/cm_admin/base/tabs.scss +26 -0
  22. data/app/assets/stylesheets/cm_admin/cm_admin.css.scss +26 -0
  23. data/app/assets/stylesheets/cm_admin/components/_alerts.scss +101 -0
  24. data/app/assets/stylesheets/cm_admin/components/_buttons.scss +135 -0
  25. data/app/assets/stylesheets/cm_admin/components/_dropdown-popup.scss +153 -0
  26. data/app/assets/stylesheets/cm_admin/components/_input.scss +274 -0
  27. data/app/assets/stylesheets/cm_admin/components/_modal.scss +34 -0
  28. data/app/assets/stylesheets/cm_admin/components/_range.scss +31 -0
  29. data/app/assets/stylesheets/cm_admin/components/_status-tag.scss +68 -0
  30. data/app/assets/stylesheets/cm_admin/components/index.scss +7 -0
  31. data/app/assets/stylesheets/cm_admin/dependency/bootstrap.min.css +7 -0
  32. data/app/assets/stylesheets/cm_admin/helpers/_mixins.scss +20 -0
  33. data/app/assets/stylesheets/cm_admin/helpers/_variable.scss +87 -0
  34. data/app/assets/stylesheets/cm_admin/helpers/index.scss +2 -0
  35. data/app/controllers/cm_admin/application_controller.rb +11 -0
  36. data/app/controllers/cm_admin/exports_controller.rb +16 -0
  37. data/app/controllers/cm_admin/static_controller.rb +12 -0
  38. data/app/helpers/cm_admin/application_helper.rb +11 -0
  39. data/app/helpers/cm_admin/custom_helper.rb +4 -0
  40. data/app/javascript/packs/cm_admin/application.js +22 -0
  41. data/app/javascript/packs/cm_admin/filters.js +347 -0
  42. data/app/javascript/packs/cm_admin/quick_search.js +67 -0
  43. data/app/javascript/packs/cm_admin/scaffolds.js +43 -0
  44. data/app/javascript/stylesheets/cm_admin/application.scss +3 -0
  45. data/app/views/cm_admin/main/_associated_table.html.slim +60 -0
  46. data/app/views/cm_admin/main/_cm_pagy_nav.html.slim +23 -0
  47. data/app/views/cm_admin/main/_filters.html.slim +1 -0
  48. data/app/views/cm_admin/main/_nested_fields.html.slim +7 -0
  49. data/app/views/cm_admin/main/_nested_table_form.html.slim +6 -0
  50. data/app/views/cm_admin/main/_table.html.slim +60 -0
  51. data/app/views/cm_admin/main/_tabs.html.slim +5 -0
  52. data/app/views/cm_admin/main/_top_navbar.html.slim +25 -0
  53. data/app/views/cm_admin/main/associated_index.html.slim +6 -0
  54. data/app/views/cm_admin/main/associated_show.html.slim +6 -0
  55. data/app/views/cm_admin/main/dashboard.html.slim +1 -0
  56. data/app/views/cm_admin/main/edit.html.slim +19 -0
  57. data/app/views/cm_admin/main/index.html.slim +12 -0
  58. data/app/views/cm_admin/main/new.html.slim +22 -0
  59. data/app/views/cm_admin/main/show.html.slim +12 -0
  60. data/app/views/cm_admin/static/error_401.html.slim +4 -0
  61. data/app/views/layouts/_left_sidebar_nav.html.slim +34 -0
  62. data/app/views/layouts/_quick_links.html.slim +25 -0
  63. data/app/views/layouts/cm_admin.html.slim +41 -0
  64. data/app/views/layouts/static.html.slim +18 -0
  65. data/bin/webpack +18 -0
  66. data/bin/webpack-dev-server +18 -0
  67. data/cm_admin.gemspec +21 -31
  68. data/config/.DS_Store +0 -0
  69. data/config/initializers/active_record_extension.rb +9 -0
  70. data/config/routes.rb +19 -0
  71. data/config/webpack/development.js +5 -0
  72. data/config/webpack/environment.js +13 -0
  73. data/config/webpack/production.js +5 -0
  74. data/config/webpack/test.js +5 -0
  75. data/config/webpacker.yml +92 -0
  76. data/lib/cm_admin/constants.rb +33 -0
  77. data/lib/cm_admin/engine.rb +38 -0
  78. data/lib/cm_admin/model.rb +152 -0
  79. data/lib/cm_admin/models/action.rb +36 -0
  80. data/lib/cm_admin/models/actions/blocks.rb +25 -0
  81. data/lib/cm_admin/models/blocks.rb +19 -0
  82. data/lib/cm_admin/models/cm_show_section.rb +20 -0
  83. data/lib/cm_admin/models/column.rb +61 -0
  84. data/lib/cm_admin/models/controller_method.rb +75 -0
  85. data/lib/cm_admin/models/dsl_method.rb +122 -0
  86. data/lib/cm_admin/models/export.rb +54 -0
  87. data/lib/cm_admin/models/field.rb +15 -0
  88. data/lib/cm_admin/models/filter.rb +95 -0
  89. data/lib/cm_admin/models/form_field.rb +21 -0
  90. data/lib/cm_admin/models/tab.rb +13 -0
  91. data/lib/cm_admin/utils.rb +67 -0
  92. data/lib/cm_admin/version.rb +1 -1
  93. data/lib/cm_admin/view_helpers/column_field_helper.rb +29 -0
  94. data/lib/cm_admin/view_helpers/field_display_helper.rb +65 -0
  95. data/lib/cm_admin/view_helpers/filter_helper.rb +190 -0
  96. data/lib/cm_admin/view_helpers/form_field_helper.rb +33 -0
  97. data/lib/cm_admin/view_helpers/form_helper.rb +65 -0
  98. data/lib/cm_admin/view_helpers/manage_column_popup_helper.rb +75 -0
  99. data/lib/cm_admin/view_helpers/navigation_helper.rb +34 -0
  100. data/lib/cm_admin/view_helpers/page_info_helper.rb +57 -0
  101. data/lib/cm_admin/view_helpers.rb +74 -0
  102. data/lib/cm_admin.rb +33 -2
  103. data/lib/generators/cm_admin/install_generator.rb +3 -13
  104. data/lib/tasks/webpack_install.rake +63 -0
  105. data/package.json +23 -0
  106. data/postcss.config.js +12 -0
  107. data/tmp/cache/webpacker/last-compilation-digest-development +1 -0
  108. data/yarn.lock +6933 -0
  109. metadata +155 -46
  110. data/lib/generators/cm_admin/USAGE +0 -8
  111. data/lib/generators/cm_admin/templates/assets/images/cm.png +0 -0
  112. data/lib/generators/cm_admin/templates/assets/stylesheets/application.css.scss +0 -33
  113. data/lib/generators/cm_admin/templates/assets/stylesheets/base/_variable.scss +0 -14
  114. data/lib/generators/cm_admin/templates/assets/stylesheets/base/input-styles.scss +0 -72
  115. data/lib/generators/cm_admin/templates/assets/stylesheets/base/main-nav.scss +0 -79
  116. data/lib/generators/cm_admin/templates/assets/stylesheets/base/scaffold.scss +0 -95
  117. data/lib/generators/cm_admin/templates/assets/stylesheets/base/show.scss +0 -88
  118. data/lib/generators/cm_admin/templates/assets/stylesheets/base/sidebar.scss +0 -69
  119. data/lib/generators/cm_admin/templates/assets/stylesheets/base/table.scss +0 -246
  120. data/lib/generators/cm_admin/templates/layouts/_navbar.html.slim +0 -8
  121. data/lib/generators/cm_admin/templates/layouts/_side_navbar.html.slim +0 -12
  122. data/lib/generators/cm_admin/templates/layouts/application.html.slim +0 -20
  123. data/lib/generators/cm_admin/templates/layouts/initializer.rb +0 -2
  124. data/lib/generators/cm_admin/templates/views/_form.erb +0 -67
  125. data/lib/generators/cm_admin/templates/views/_table.erb +0 -22
  126. data/lib/generators/cm_admin/templates/views/edit.erb +0 -10
  127. data/lib/generators/cm_admin/templates/views/index.erb +0 -11
  128. data/lib/generators/cm_admin/templates/views/new.erb +0 -10
  129. data/lib/generators/cm_admin/templates/views/reset_password.erb +0 -12
  130. data/lib/generators/cm_admin/templates/views/show.erb +0 -18
  131. data/lib/generators/cm_admin/templates/views/sign_in.erb +0 -18
  132. data/lib/generators/cm_admin/view_generator.rb +0 -78
@@ -0,0 +1,152 @@
1
+ require_relative 'constants'
2
+ require_relative 'models/action'
3
+ require_relative 'models/field'
4
+ require_relative 'models/form_field'
5
+ require_relative 'models/blocks'
6
+ require_relative 'models/column'
7
+ require_relative 'models/filter'
8
+ require_relative 'models/export'
9
+ require_relative 'models/cm_show_section'
10
+ require_relative 'models/tab'
11
+ require_relative 'models/dsl_method'
12
+ require_relative 'models/controller_method'
13
+ require 'pagy'
14
+ require 'axlsx'
15
+ require 'cocoon'
16
+
17
+ module CmAdmin
18
+ class Model
19
+ include Pagy::Backend
20
+ include Models::Blocks
21
+ include Models::DslMethod
22
+ include Models::ControllerMethod
23
+ attr_accessor :available_actions, :actions_set, :available_fields, :permitted_fields, :current_action, :params, :filters, :available_tabs
24
+ attr_reader :name, :ar_model
25
+
26
+ # Class variable for storing all actions
27
+ # CmAdmin::Model.all_actions
28
+ singleton_class.send(:attr_accessor, :all_actions)
29
+
30
+ def initialize(entity, &block)
31
+ @name = entity.name
32
+ @ar_model = entity
33
+ @available_actions ||= []
34
+ @current_action = nil
35
+ @available_tabs ||= []
36
+ @available_fields ||= {index: [], show: [], edit: {fields: []}, new: {fields: []}}
37
+ @params = nil
38
+ @filters ||= []
39
+ instance_eval(&block) if block_given?
40
+ actions unless @actions_set
41
+ $available_actions = @available_actions.dup
42
+ self.class.all_actions.push(@available_actions)
43
+ define_controller
44
+ end
45
+
46
+ class << self
47
+ def all_actions
48
+ @all_actions || []
49
+ end
50
+
51
+ def find_by(search_hash)
52
+ CmAdmin.cm_admin_models.find { |x| x.name == search_hash[:name] }
53
+ end
54
+ end
55
+
56
+ def custom_controller_action(action_name, params)
57
+ current_action = CmAdmin::Models::Action.find_by(self, name: action_name.to_s)
58
+ if current_action
59
+ @current_action = current_action
60
+ @ar_object = @ar_model.find(params[:id])
61
+ if @current_action.child_records
62
+ child_records = @ar_object.send(@current_action.child_records)
63
+ @associated_model = CmAdmin::Model.find_by(name: @ar_model.reflect_on_association(@current_action.child_records).klass.name)
64
+ if child_records.is_a? ActiveRecord::Relation
65
+ @associated_ar_object = filter_by(params, child_records)
66
+ else
67
+ @associated_ar_object = child_records
68
+ end
69
+ return @ar_object, @associated_model, @associated_ar_object
70
+ end
71
+ return @ar_object
72
+ end
73
+ end
74
+
75
+ # Insert into actions according to config block
76
+ def actions(only: [], except: [])
77
+ acts = CmAdmin::DEFAULT_ACTIONS.keys
78
+ acts = acts & (Array.new << only).flatten if only.present?
79
+ acts = acts - (Array.new << except).flatten if except.present?
80
+ acts.each do |act|
81
+ action_defaults = CmAdmin::DEFAULT_ACTIONS[act]
82
+ @available_actions << CmAdmin::Models::Action.new(name: act.to_s, verb: action_defaults[:verb], path: action_defaults[:path])
83
+ end
84
+ @actions_set = true
85
+ end
86
+
87
+
88
+ private
89
+
90
+ # Controller defined for each model
91
+ # If model is User, controller will be UsersController
92
+ def define_controller
93
+ klass = Class.new(CmAdmin::ApplicationController) do
94
+
95
+ $available_actions.each do |action|
96
+ define_method action.name.to_sym do
97
+
98
+ # controller_name & action_name from ActionController
99
+ @model = CmAdmin::Model.find_by(name: controller_name.classify)
100
+ @model.params = params
101
+ @action = CmAdmin::Models::Action.find_by(@model, name: action_name)
102
+ @ar_object = @model.try(action_name, params)
103
+ @ar_object, @associated_model, @associated_ar_object = @model.custom_controller_action(action_name, params.permit!) if !@ar_object.present? && params[:id].present?
104
+ nested_tables = @model.available_fields[:new].except(:fields).keys
105
+ nested_tables += @model.available_fields[:edit].except(:fields).keys
106
+ @reflections = @model.ar_model.reflect_on_all_associations
107
+ nested_tables.each do |table_name|
108
+ reflection = @reflections.select {|x| x if x.name == table_name}.first
109
+ if reflection.macro == :has_many
110
+ @ar_object.send(table_name).build if action_name == "new" || action_name == "edit"
111
+ else
112
+ @ar_object.send(('build_' + table_name.to_s).to_sym) if action_name == "new"
113
+ end
114
+ end
115
+ respond_to do |format|
116
+ if %w(show index new edit).include?(action_name)
117
+ if request.xhr? && action_name.eql?('index')
118
+ format.html { render partial: '/cm_admin/main/table' }
119
+ else
120
+ format.html { render '/cm_admin/main/'+action_name }
121
+ end
122
+ elsif %w(create update destroy).include?(action_name)
123
+ if @ar_object.save
124
+ format.html { redirect_to CmAdmin::Engine.mount_path + "/#{@model.name.underscore.pluralize}" }
125
+ else
126
+ format.html { render '/cm_admin/main/new' }
127
+ end
128
+ elsif action.layout.present?
129
+ if request.xhr? && action.partial.present?
130
+ format.html { render partial: action.partial }
131
+ else
132
+ format.html { render action.layout }
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end if $available_actions.present?
139
+ CmAdmin.const_set "#{@name}Controller", klass
140
+ end
141
+
142
+ def filter_params(params)
143
+ # OPTIMIZE: Need to check if we can permit the filter_params in a better way
144
+ date_columns = @filters.select{|x| x.filter_type.eql?(:date)}.map(&:db_column_name)
145
+ range_columns = @filters.select{|x| x.filter_type.eql?(:range)}.map(&:db_column_name)
146
+ single_select_columns = @filters.select{|x| x.filter_type.eql?(:single_select)}.map(&:db_column_name)
147
+ multi_select_columns = @filters.select{|x| x.filter_type.eql?(:multi_select)}.map{|x| Hash["#{x.db_column_name}", []]}
148
+
149
+ params.require(:filters).permit(:search, date: date_columns, range: range_columns, single_select: single_select_columns, multi_select: multi_select_columns) if params[:filters]
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,36 @@
1
+ require_relative 'actions/blocks'
2
+
3
+ module CmAdmin
4
+ module Models
5
+ class Action
6
+ include Actions::Blocks
7
+ attr_accessor :name, :verb, :layout_type, :layout, :partial, :path, :page_title, :page_description, :child_records, :is_nested_field, :nested_table_name
8
+
9
+ def initialize(attributes = {})
10
+ if attributes[:layout_type].present? && attributes[:layout].nil? && attributes[:partial].nil?
11
+ case attributes[:layout_type]
12
+ when 'cm_association_index'
13
+ attributes[:layout] = '/cm_admin/main/associated_index'
14
+ attributes[:partial] = '/cm_admin/main/associated_table'
15
+ when 'cm_association_show'
16
+ attributes[:layout] = '/cm_admin/main/associated_show'
17
+ end
18
+ end
19
+ set_default_values
20
+ attributes.each do |key, value|
21
+ self.send("#{key.to_s}=", value)
22
+ end
23
+ end
24
+
25
+ def set_default_values
26
+ self.is_nested_field = false
27
+ end
28
+
29
+ class << self
30
+ def find_by(model, search_hash)
31
+ model.available_actions.find { |i| i.name == search_hash[:name] }
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,25 @@
1
+ module CmAdmin
2
+ module Models
3
+ module Actions
4
+ module Blocks
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ attr_accessor :title
9
+ end
10
+
11
+ def set_layout(name)
12
+ self.layout = name
13
+ end
14
+
15
+ def set_partial(name)
16
+ self.partial = name
17
+ end
18
+
19
+ def set_title(name)
20
+ self.title = name
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,19 @@
1
+ module CmAdmin
2
+ module Models
3
+ module Blocks
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ attr_accessor :menu, :title
8
+ end
9
+
10
+ def set_menu(state)
11
+ self.menu = state.to_s == 'on' ? true : false
12
+ end
13
+
14
+ def set_title(name)
15
+ self.title = name
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,20 @@
1
+ module CmAdmin
2
+ module Models
3
+ class CmShowSection
4
+
5
+ attr_accessor :section_name, :available_section_fields
6
+
7
+ def initialize(section_name, &block)
8
+ @available_section_fields = []
9
+ @section_name = section_name
10
+ puts "-- reached here --"
11
+ instance_eval(&block)
12
+ end
13
+
14
+ def field(field_name, options={})
15
+ puts "For printing field #{field_name}"
16
+ @available_section_fields << CmAdmin::Models::Field.new(field_name, options)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,61 @@
1
+ module CmAdmin
2
+ module Models
3
+ class Column
4
+ attr_accessor :field_name, :field_type, :header, :format, :prefix, :suffix, :exportable, :round,
5
+ :cm_css_class, :link, :url, :custom_method, :helper_method, :managable, :lockable
6
+
7
+ def initialize(field_name, attributes = {})
8
+ @field_name = field_name
9
+ set_default_values
10
+ attributes.each do |key, value|
11
+ self.send("#{key.to_s}=", value)
12
+ end
13
+
14
+ #formatting header (either field_name or value present in header attribute)
15
+ self.send("header=", format_header)
16
+ end
17
+
18
+ #returns a string value as a header (either field_name or value present in header attribute)
19
+ def format_header
20
+ self.header.present? ? self.header.to_s.gsub(/_/, ' ')&.upcase : self.field_name.to_s.gsub(/_/, ' ').upcase
21
+ end
22
+
23
+ def set_default_values
24
+ self.exportable = true
25
+ self.managable = true
26
+ self.lockable = false
27
+ end
28
+
29
+ #formatting value for different data types
30
+ def self.format_data_type(column, value)
31
+ case column.column_type
32
+ when :string
33
+ if column.format.present?
34
+ column.format = [column.format] if column.format.is_a? String
35
+ column.format.each do |formatter|
36
+ value = value.send(formatter)
37
+ end
38
+ end
39
+ when :datetime
40
+ format_value = column.format.present? ? column.format.to_s : '%d/%m/%Y'
41
+ value = value.strftime(format_value)
42
+ when :enum
43
+ value = value.titleize
44
+ when :decimal
45
+ round_to = column.round.present? ? column.round.to_i : 2
46
+ value = value.round(round_to)
47
+ when :custom
48
+
49
+ end
50
+ return value
51
+ end
52
+
53
+ class << self
54
+ def find_by(model, search_hash)
55
+ model.available_fields.find { |i| i.name == search_hash[:name] }
56
+ end
57
+ end
58
+
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,75 @@
1
+ module CmAdmin
2
+ module Models
3
+ module ControllerMethod
4
+ extend ActiveSupport::Concern
5
+
6
+ def show(params)
7
+ @current_action = CmAdmin::Models::Action.find_by(self, name: 'show')
8
+ @ar_object = @ar_model.find(params[:id])
9
+ end
10
+
11
+ def index(params)
12
+ @current_action = CmAdmin::Models::Action.find_by(self, name: 'index')
13
+ # Based on the params the filter and pagination object to be set
14
+ @ar_object = filter_by(params, nil, filter_params(params))
15
+ end
16
+
17
+ def new(params)
18
+ @current_action = CmAdmin::Models::Action.find_by(self, name: 'new')
19
+ @ar_object = @ar_model.new
20
+ end
21
+
22
+ def edit(params)
23
+ @current_action = CmAdmin::Models::Action.find_by(self, name: 'edit')
24
+ @ar_object = @ar_model.find(params[:id])
25
+ end
26
+
27
+ def update(params)
28
+ @ar_object = @ar_model.find(params[:id])
29
+ @ar_object.assign_attributes(resource_params(params))
30
+ @ar_object
31
+ end
32
+
33
+ def create(params)
34
+ @ar_object = @ar_model.new(resource_params(params))
35
+ end
36
+
37
+ def filter_by(params, records, filter_params={}, sort_params={})
38
+ filtered_result = OpenStruct.new
39
+ sort_column = "created_at"
40
+ sort_direction = %w[asc desc].include?(sort_params[:sort_direction]) ? sort_params[:sort_direction] : "asc"
41
+ sort_params = {sort_column: sort_column, sort_direction: sort_direction}
42
+ records = self.name.constantize.where(nil) if records.nil?
43
+ final_data = CmAdmin::Models::Filter.filtered_data(filter_params, records, @filters)
44
+ pagy, records = pagy(final_data)
45
+ filtered_result.data = records
46
+ filtered_result.pagy = pagy
47
+ # filtered_result.facets = paginate(page, raw_data.size)
48
+ # filtered_result.sort = sort_params
49
+ # filtered_result.facets.sort = sort_params
50
+ return filtered_result
51
+ end
52
+
53
+ def resource_params(params)
54
+ permittable_fields = @permitted_fields || @ar_model.columns.map(&:name).reject { |i| CmAdmin::REJECTABLE_FIELDS.include?(i) }.map(&:to_sym)
55
+ permittable_fields += @ar_model.reflect_on_all_attachments.map {|x|
56
+ if x.class.name.include?('HasOne')
57
+ x.name
58
+ elsif x.class.name.include?('HasMany')
59
+ Hash[x.name.to_s, []]
60
+ end
61
+ }.compact
62
+ nested_tables = self.available_fields[:new].except(:fields).keys
63
+ nested_tables += self.available_fields[:edit].except(:fields).keys
64
+ nested_fields = nested_tables.map {|table|
65
+ Hash[
66
+ table.to_s + '_attributes',
67
+ table.to_s.singularize.titleize.constantize.columns.map(&:name).reject { |i| CmAdmin::REJECTABLE_FIELDS.include?(i) }.map(&:to_sym) + [:id, :_destroy]
68
+ ]
69
+ }
70
+ permittable_fields += nested_fields
71
+ params.require(self.name.underscore.to_sym).permit(*permittable_fields)
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,122 @@
1
+ module CmAdmin
2
+ module Models
3
+ module DslMethod
4
+ extend ActiveSupport::Concern
5
+
6
+ def cm_index(page_title: nil ,page_description: nil, &block)
7
+ @current_action = CmAdmin::Models::Action.find_by(self, name: 'index')
8
+ @current_action.page_title = page_title
9
+ @current_action.page_description = page_description
10
+ yield
11
+ # action.instance_eval(&block)
12
+ end
13
+
14
+ def cm_show(page_title: nil,page_description: nil,&block)
15
+ @current_action = CmAdmin::Models::Action.find_by(self, name: 'show')
16
+ @current_action.page_title = page_title
17
+ @current_action.page_description = page_description
18
+ yield
19
+ end
20
+
21
+ def cm_edit(page_title: nil,page_description: nil, &block)
22
+ @current_action = CmAdmin::Models::Action.find_by(self, name: 'edit')
23
+ @current_action.page_title = page_title
24
+ @current_action.page_description = page_description
25
+ yield
26
+ end
27
+
28
+ def cm_new(page_title: nil,page_description: nil,&block)
29
+ @current_action = CmAdmin::Models::Action.find_by(self, name: 'new')
30
+ @current_action.page_title = page_title
31
+ @current_action.page_description = page_description
32
+ yield
33
+ end
34
+
35
+ def page_title(title)
36
+ if @current_action
37
+ @current_action.page_title = title
38
+ end
39
+ end
40
+
41
+ def page_description(description)
42
+ if @current_action
43
+ @current_action.page_description = description
44
+ end
45
+ end
46
+
47
+ def tab(tab_name, custom_action, associated_model: nil, layout_type: nil, layout: nil, partial: nil, &block)
48
+ if custom_action.to_s == ''
49
+ @current_action = CmAdmin::Models::Action.find_by(self, name: 'show')
50
+ @available_tabs << CmAdmin::Models::Tab.new(tab_name, '', &block)
51
+ else
52
+ action = CmAdmin::Models::Action.new(name: custom_action.to_s, verb: :get, path: ':id/'+custom_action, layout_type: layout_type, layout: layout, partial: partial, child_records: associated_model)
53
+ @available_actions << action
54
+ @current_action = action
55
+ @available_tabs << CmAdmin::Models::Tab.new(tab_name, custom_action, &block)
56
+ end
57
+ yield if block
58
+ end
59
+
60
+ def cm_show_section(section_name, &block)
61
+ @available_fields[@current_action.name.to_sym] ||= []
62
+ @available_fields[@current_action.name.to_sym] << CmAdmin::Models::CmShowSection.new(section_name, &block)
63
+ end
64
+
65
+ def form_field(field_name, options={}, arg=nil)
66
+ unless @current_action.is_nested_field
67
+ @available_fields[@current_action.name.to_sym][:fields] << CmAdmin::Models::FormField.new(field_name, options[:input_type], options)
68
+ else
69
+ @available_fields[@current_action.name.to_sym][@current_action.nested_table_name] ||= []
70
+ @available_fields[@current_action.name.to_sym][@current_action.nested_table_name] << CmAdmin::Models::FormField.new(field_name, options[:input_type], options)
71
+ end
72
+ end
73
+
74
+ def nested_form_field(field_name, &block)
75
+ @current_action.is_nested_field = true
76
+ @current_action.nested_table_name = field_name
77
+ yield
78
+ end
79
+
80
+ def column(field_name, options={})
81
+ @available_fields[@current_action.name.to_sym] ||= []
82
+ if @available_fields[@current_action.name.to_sym].select{|x| x.lockable}.size > 0 && options[:lockable]
83
+ raise "Only one column can be locked in a table."
84
+ end
85
+ unless @available_fields[@current_action.name.to_sym].map{|x| x.field_name.to_sym}.include?(field_name)
86
+ @available_fields[@current_action.name.to_sym] << CmAdmin::Models::Column.new(field_name, options)
87
+ end
88
+ end
89
+
90
+ def all_db_columns(options={})
91
+ field_names = self.instance_variable_get(:@ar_model)&.columns&.map{|x| x.name.to_sym}
92
+ if options.include?(:exclude) && field_names
93
+ excluded_fields = (Array.new << options[:exclude]).flatten.map(&:to_sym)
94
+ field_names -= excluded_fields
95
+ end
96
+ field_names.each do |field_name|
97
+ column field_name
98
+ end
99
+ end
100
+
101
+ # Custom actions
102
+ # eg
103
+ # class User < ApplicationRecord
104
+ # cm_admin do
105
+ # custom_action name: 'submit', verb: 'post', path: ':id/submit' do
106
+ # def user_submit
107
+ # Code for action here...
108
+ # end
109
+ # end
110
+ # end
111
+ # end
112
+ def custom_action(name: nil, verb: nil, layout: nil, partial: nil, path: nil, &block)
113
+ @available_actions << CmAdmin::Models::Action.new(name: name, verb: verb, layout: layout, partial: partial, path: path)
114
+ self.class.class_eval(&block)
115
+ end
116
+
117
+ def filter(db_column_name, filter_type, options={})
118
+ @filters << CmAdmin::Models::Filter.new(db_column_name: db_column_name, filter_type: filter_type, options: options)
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,54 @@
1
+ module CmAdmin
2
+ module Models
3
+ class Export
4
+ class << self
5
+ def generate_excel(klass_name, columns = [], helpers)
6
+ klass = klass_name.constantize
7
+ model = CmAdmin::Model.find_by({name: klass_name})
8
+ records = get_records(klass, model, columns, helpers)
9
+ file_path = "#{Rails.root}/tmp/#{klass}_data_#{DateTime.now.strftime("%Y-%m-%d_%H-%M-%S")}.xlsx"
10
+ create_workbook(records, columns, file_path)
11
+ return file_path
12
+ end
13
+
14
+ def get_records(klass, model, columns, helpers)
15
+ records = klass
16
+ custom_fields = model.available_fields[:index].map{|field| field if field.field_type == :custom}.compact
17
+ normal_fields = model.available_fields[:index].map{|field| field unless field.field_type == :custom}.compact
18
+ deserialized_columns = CmAdmin::Utils.deserialize_csv_columns(columns, :as_json_params)
19
+ # This includes isn't recursve, a full solution should be recursive
20
+ records_arr = []
21
+ records.includes(deserialized_columns[:include].keys).find_each do |record|
22
+ record_hash = record.as_json({only: normal_fields.map(&:field_name)})
23
+ custom_fields.each do |field|
24
+ record_hash[field.field_name.to_sym] = helpers.send(field.helper_method, record, field.field_name)
25
+ end
26
+ records_arr << record_hash
27
+ end
28
+ records_arr
29
+ end
30
+
31
+ def create_workbook(records, class_name, file_path)
32
+ flattened_records = records.map { |record| CmAdmin::Utils.flatten_hash(record) }
33
+ columns = flattened_records.map{|x| x.keys}.flatten.uniq.sort
34
+ size_arr = []
35
+ columns.size.times { size_arr << 22 }
36
+ xl = Axlsx::Package.new
37
+ xl.workbook.add_worksheet do |sheet|
38
+ sheet.add_row columns&.map(&:titleize), b: true
39
+ flattened_records.each do |record|
40
+ sheet.add_row(columns.map { |column| record[column] })
41
+ end
42
+ sheet.column_widths(*size_arr)
43
+ end
44
+ xl.serialize(file_path)
45
+ end
46
+
47
+ def exportable_columns(klass)
48
+ klass.available_fields[:index].map{|x| x.exportable ? x.field_name : ""}.reject { |c| c.empty? }
49
+ end
50
+
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,15 @@
1
+ module CmAdmin
2
+ module Models
3
+ class Field
4
+
5
+ attr_accessor :field_name, :label, :header, :field_type, :format, :precision, :helper_method, :preview, :custom_link, :precision
6
+
7
+ def initialize(field_name, attributes = {})
8
+ @field_name = field_name
9
+ attributes.each do |key, value|
10
+ self.send("#{key.to_s}=", value)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,95 @@
1
+ module CmAdmin
2
+ module Models
3
+ class Filter
4
+ attr_accessor :db_column_name, :filter_type, :placeholder, :collection
5
+
6
+ VALID_FILTER_TYPES = Set[:date, :multi_select, :range, :search, :single_select].freeze
7
+
8
+ def initialize(db_column_name:, filter_type:, options: {})
9
+ raise TypeError, "Can't have array of multiple columns for #{filter_type} filter" if db_column_name.is_a?(Array) && db_column_name.size > 1 && !filter_type.to_sym.eql?(:search)
10
+ raise ArgumentError, "Kindly select a valid filter type like #{VALID_FILTER_TYPES.sort.to_sentence(last_word_connector: ', or ')} instead of #{filter_type} for column #{db_column_name}" unless VALID_FILTER_TYPES.include?(filter_type.to_sym)
11
+ @db_column_name, @filter_type = structure_data(db_column_name, filter_type)
12
+ options.each do |key, value|
13
+ self.send("#{key.to_s}=", value)
14
+ end
15
+ end
16
+
17
+ def structure_data(db_column_name, filter_type)
18
+ filter_type = filter_type.is_a?(Array) ? filter_type[0].to_sym : filter_type.to_sym
19
+
20
+ case filter_type
21
+ when :search
22
+ db_column_name = (Array.new << db_column_name).flatten.map(&:to_sym)
23
+ else
24
+ db_column_name = db_column_name.is_a?(Array) ? db_column_name[0].to_sym : db_column_name.to_sym
25
+ end
26
+ [db_column_name, filter_type]
27
+ end
28
+
29
+ # Methods to filter the records based on the filter type.
30
+ class << self
31
+ def filtered_data(filter_params, records, filters)
32
+ if filter_params
33
+ filter_params.each do |scope_type, scope_value|
34
+ scope_name = if scope_type.eql?('date') || scope_type.eql?('range')
35
+ 'date_and_range'
36
+ elsif scope_type.eql?('single_select') || scope_type.eql?('multi_select')
37
+ 'dropdown'
38
+ else
39
+ scope_type
40
+ end
41
+ records = self.send("cm_#{scope_name}_filter", scope_value, records, filters) if scope_value.present?
42
+ end
43
+ end
44
+ records
45
+ end
46
+
47
+ def cm_search_filter(scope_value, records, filters)
48
+ return nil if scope_value.blank?
49
+ table_name = records.table_name
50
+
51
+ filters.select{|x| x if x.filter_type.eql?(:search)}.each do |filter|
52
+ terms = scope_value.downcase.split(/\s+/)
53
+ terms = terms.map { |e|
54
+ (e.gsub('*', '%').prepend('%') + '%').gsub(/%+/, '%')
55
+ }
56
+ sql = ""
57
+ filter.db_column_name.each.with_index do |column, i|
58
+ sql.concat("#{table_name}.#{column} ILIKE ?")
59
+ sql.concat(' OR ') unless filter.db_column_name.size.eql?(i+1)
60
+ end
61
+
62
+ records = records.where(
63
+ terms.map { |term|
64
+ sql
65
+ }.join(' AND '),
66
+ *terms.map { |e| [e] * filter.db_column_name.size }.flatten
67
+ )
68
+ end
69
+ records
70
+ end
71
+
72
+ def cm_date_and_range_filter(scope_value, records, filters)
73
+ return nil if scope_value.nil?
74
+ scope_value.each do |key, value|
75
+ if value.present?
76
+ value = value.split(' to ')
77
+ from = value[0].presence
78
+ to = value[1].presence
79
+ records = records.where(key => from..to)
80
+ end
81
+ end
82
+ records
83
+ end
84
+
85
+ def cm_dropdown_filter(scope_value, records, filters)
86
+ return nil if scope_value.nil?
87
+ scope_value.each do |key, value|
88
+ records = records.where(key => value) if value.present?
89
+ end
90
+ records
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end