cm-admin 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/CODE_OF_CONDUCT.md +74 -0
  3. data/Gemfile +1 -0
  4. data/Gemfile.lock +21 -20
  5. data/README.md +24 -1
  6. data/app/assets/stylesheets/cm_admin/base/auth.scss +0 -12
  7. data/app/assets/stylesheets/cm_admin/base/filters.scss +8 -7
  8. data/app/assets/stylesheets/cm_admin/base/form.scss +7 -72
  9. data/app/assets/stylesheets/cm_admin/base/navbar.scss +1 -3
  10. data/app/assets/stylesheets/cm_admin/base/quicksearch.scss +7 -0
  11. data/app/assets/stylesheets/cm_admin/base/scaffold.scss +38 -3
  12. data/app/assets/stylesheets/cm_admin/base/show.scss +24 -2
  13. data/app/assets/stylesheets/cm_admin/base/table.scss +270 -253
  14. data/app/assets/stylesheets/cm_admin/components/_dropdown-popup.scss +22 -10
  15. data/app/assets/stylesheets/cm_admin/components/_range.scss +13 -2
  16. data/app/assets/stylesheets/cm_admin/components/_status-tag.scss +2 -1
  17. data/app/assets/stylesheets/cm_admin/helpers/_variable.scss +1 -0
  18. data/app/controllers/cm_admin/application_controller.rb +5 -0
  19. data/app/controllers/cm_admin/exports_controller.rb +1 -1
  20. data/app/controllers/cm_admin/static_controller.rb +12 -0
  21. data/app/helpers/cm_admin/custom_helper.rb +4 -0
  22. data/app/javascript/packs/cm_admin/application.js +14 -1
  23. data/app/javascript/packs/cm_admin/filters.js +331 -16
  24. data/app/javascript/packs/cm_admin/quick_search.js +67 -0
  25. data/app/javascript/packs/cm_admin/scaffolds.js +30 -1
  26. data/app/javascript/stylesheets/cm_admin/application.scss +3 -0
  27. data/app/views/cm_admin/main/_associated_table.html.slim +60 -0
  28. data/app/views/cm_admin/main/_cm_pagy_nav.html.slim +6 -6
  29. data/app/views/cm_admin/main/_filters.html.slim +1 -10
  30. data/app/views/cm_admin/main/_nested_fields.html.slim +7 -0
  31. data/app/views/cm_admin/main/_nested_table_form.html.slim +6 -0
  32. data/app/views/cm_admin/main/_table.html.slim +5 -4
  33. data/app/views/cm_admin/main/_tabs.html.slim +5 -0
  34. data/app/views/cm_admin/main/_top_navbar.html.slim +4 -4
  35. data/app/views/cm_admin/main/associated_index.html.slim +6 -0
  36. data/app/views/cm_admin/main/associated_show.html.slim +6 -0
  37. data/app/views/cm_admin/main/index.html.slim +11 -8
  38. data/app/views/cm_admin/main/show.html.slim +12 -13
  39. data/app/views/cm_admin/static/error_401.html.slim +4 -0
  40. data/app/views/layouts/_left_sidebar_nav.html.slim +7 -3
  41. data/app/views/layouts/_quick_links.html.slim +25 -0
  42. data/app/views/layouts/cm_admin.html.slim +24 -1
  43. data/app/views/layouts/static.html.slim +18 -0
  44. data/bin/console +0 -1
  45. data/cm_admin.gemspec +1 -0
  46. data/config/routes.rb +2 -1
  47. data/config/webpack/environment.js +3 -2
  48. data/lib/.DS_Store +0 -0
  49. data/lib/cm_admin/model.rb +57 -167
  50. data/lib/cm_admin/models/action.rb +15 -1
  51. data/lib/cm_admin/models/cm_show_section.rb +20 -0
  52. data/lib/cm_admin/models/column.rb +49 -4
  53. data/lib/cm_admin/models/controller_method.rb +75 -0
  54. data/lib/cm_admin/models/dsl_method.rb +122 -0
  55. data/lib/cm_admin/models/export.rb +16 -5
  56. data/lib/cm_admin/models/field.rb +2 -1
  57. data/lib/cm_admin/models/filter.rb +67 -2
  58. data/lib/cm_admin/models/form_field.rb +21 -0
  59. data/lib/cm_admin/models/tab.rb +13 -0
  60. data/lib/cm_admin/version.rb +1 -1
  61. data/lib/cm_admin/view_helpers/column_field_helper.rb +29 -0
  62. data/lib/cm_admin/view_helpers/field_display_helper.rb +65 -0
  63. data/lib/cm_admin/view_helpers/filter_helper.rb +190 -0
  64. data/lib/cm_admin/view_helpers/form_field_helper.rb +21 -4
  65. data/lib/cm_admin/view_helpers/form_helper.rb +22 -12
  66. data/lib/cm_admin/view_helpers/manage_column_popup_helper.rb +75 -0
  67. data/lib/cm_admin/view_helpers/navigation_helper.rb +22 -6
  68. data/lib/cm_admin/view_helpers/page_info_helper.rb +36 -0
  69. data/lib/cm_admin/view_helpers.rb +3 -1
  70. data/lib/cm_admin.rb +3 -1
  71. data/lib/generators/cm_admin/install_generator.rb +20 -8
  72. data/package.json +6 -1
  73. data/tmp/cache/webpacker/last-compilation-digest-development +1 -0
  74. data/yarn.lock +35 -5
  75. metadata +41 -7
  76. data/app/controllers/cm_admin/main_controller.rb +0 -8
  77. data/lib/c.png +0 -0
  78. data/lib/cm_admin/view_helpers/field_column_helper.rb +0 -23
  79. data/lib/generators/cm_admin/templates/cm_admin_initializer.rb +0 -4
  80. data/yarn-error.log +0 -44
@@ -0,0 +1,18 @@
1
+ doctype html
2
+ html
3
+ head
4
+ title
5
+ | Admin
6
+ meta[name="viewport" content="width=device-width,initial-scale=1"]
7
+ = csrf_meta_tags
8
+ = csp_meta_tag
9
+ = stylesheet_link_tag 'cm_admin/cm_admin', media: 'all', 'data-turbolinks-track': 'reload'
10
+ = stylesheet_pack_tag 'cm_admin/application', 'data-turbolinks-track': 'reload'
11
+ = javascript_pack_tag 'cm_admin/application', 'data-turbolinks-track': 'reload'
12
+ body
13
+ - flash.each do |type, msg|
14
+ .alert class="alert-#{type}"
15
+ = msg
16
+ .cm-admin-static
17
+ .d-flex.flex-column.h-100
18
+ = yield
data/bin/console CHANGED
@@ -1,7 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require "bundler/setup"
4
- require "cm_admin"
5
4
 
6
5
  # You can add fixtures and/or initialization code here to make experimenting
7
6
  # with your gem easier. You can also use a different console, if you like.
data/cm_admin.gemspec CHANGED
@@ -30,4 +30,5 @@ Gem::Specification.new do |spec|
30
30
  spec.add_runtime_dependency 'slim', '~> 4.1.0'
31
31
  spec.add_runtime_dependency 'webpacker', '~> 5.2.1'
32
32
  spec.add_runtime_dependency 'axlsx_rails', '~> 0.6.1'
33
+ spec.add_runtime_dependency 'cocoon', '~> 1.2.15'
33
34
  end
data/config/routes.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  CmAdmin::Engine.routes.draw do
2
- controller 'main' do
2
+ controller 'static' do
3
3
  get '/', action: 'dashboard'
4
+ get '/access-denied', action: 'error_403'
4
5
  end
5
6
 
6
7
  controller 'exports' do
@@ -3,8 +3,9 @@ const { environment } = require('@rails/webpacker')
3
3
  const webpack = require('webpack')
4
4
  environment.plugins.prepend('Provide',
5
5
  new webpack.ProvidePlugin({
6
- $: 'jquery/src/jquery',
7
- jQuery: 'jquery/src/jquery',
6
+ $: 'jquery',
7
+ jQuery: 'jquery',
8
+ moment: 'moment',
8
9
  Popper: ['popper.js', 'default']
9
10
  })
10
11
  )
data/lib/.DS_Store ADDED
Binary file
@@ -1,19 +1,26 @@
1
1
  require_relative 'constants'
2
2
  require_relative 'models/action'
3
3
  require_relative 'models/field'
4
+ require_relative 'models/form_field'
4
5
  require_relative 'models/blocks'
5
6
  require_relative 'models/column'
6
7
  require_relative 'models/filter'
7
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'
8
13
  require 'pagy'
9
14
  require 'axlsx'
10
-
15
+ require 'cocoon'
11
16
 
12
17
  module CmAdmin
13
18
  class Model
14
19
  include Pagy::Backend
15
20
  include Models::Blocks
16
- attr_accessor :available_actions, :actions_set, :available_fields, :permitted_fields, :current_action, :params, :filters
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
17
24
  attr_reader :name, :ar_model
18
25
 
19
26
  # Class variable for storing all actions
@@ -25,7 +32,8 @@ module CmAdmin
25
32
  @ar_model = entity
26
33
  @available_actions ||= []
27
34
  @current_action = nil
28
- @available_fields ||= {index: [], show: [], edit: [], new: []}
35
+ @available_tabs ||= []
36
+ @available_fields ||= {index: [], show: [], edit: {fields: []}, new: {fields: []}}
29
37
  @params = nil
30
38
  @filters ||= []
31
39
  instance_eval(&block) if block_given?
@@ -39,6 +47,29 @@ module CmAdmin
39
47
  def all_actions
40
48
  @all_actions || []
41
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
42
73
  end
43
74
 
44
75
  # Insert into actions according to config block
@@ -53,166 +84,7 @@ module CmAdmin
53
84
  @actions_set = true
54
85
  end
55
86
 
56
- def cm_show(&block)
57
- @current_action = CmAdmin::Models::Action.find_by(self, name: 'show')
58
- puts "Top of the line"
59
- yield
60
- # action.instance_eval(&block)
61
- puts "End of the line"
62
- end
63
-
64
- def cm_index(&block)
65
- @current_action = CmAdmin::Models::Action.find_by(self, name: 'index')
66
- yield
67
- # action.instance_eval(&block)
68
- end
69
-
70
- def cm_edit(&block)
71
- action = CmAdmin::Models::Action.find_by(self, name: 'edit')
72
- action.instance_eval(&block)
73
- end
74
-
75
- def show(params)
76
- @current_action = CmAdmin::Models::Action.find_by(self, name: 'show')
77
- @ar_object = @ar_model.find(params[:id])
78
- end
79
-
80
- def index(params)
81
- @current_action = CmAdmin::Models::Action.find_by(self, name: 'index')
82
- # Based on the params the filter and pagination object to be set
83
- @ar_object = filter_by(params, filter_params=filter_params(params))
84
- end
85
-
86
- def filter_by(params, filter_params={}, sort_params={})
87
- filtered_result = OpenStruct.new
88
- sort_column = "users.created_at"
89
- sort_direction = %w[asc desc].include?(sort_params[:sort_direction]) ? sort_params[:sort_direction] : "asc"
90
- sort_params = {sort_column: sort_column, sort_direction: sort_direction}
91
- final_data = filtered_data(filter_params)
92
- pagy, records = pagy(final_data)
93
- filtered_result.data = records
94
- filtered_result.pagy = pagy
95
- # filtered_result.facets = paginate(page, raw_data.size)
96
- # filtered_result.sort = sort_params
97
- # filtered_result.facets.sort = sort_params
98
- return filtered_result
99
- end
100
-
101
- def filtered_data(filter_params)
102
- records = self.name.constantize.where(nil)
103
- if filter_params
104
- filter_params.each do |scope, scope_value|
105
- records = self.send("cm_#{scope}", scope_value, records)
106
- end
107
- end
108
- records
109
- end
110
-
111
- def cm_search(scope_value, records)
112
- return nil if scope_value.blank?
113
- table_name = records.table_name
114
-
115
- @filters.select{|x| x if x.filter_type.eql?(:search)}.each do |filter|
116
- terms = scope_value.downcase.split(/\s+/)
117
- terms = terms.map { |e|
118
- (e.gsub('*', '%').prepend('%') + '%').gsub(/%+/, '%')
119
- }
120
- sql = ""
121
- filter.db_column_name.each.with_index do |column, i|
122
- sql.concat("#{table_name}.#{column} ILIKE ?")
123
- sql.concat(' OR ') unless filter.db_column_name.size.eql?(i+1)
124
- end
125
-
126
- records = records.where(
127
- terms.map { |term|
128
- sql
129
- }.join(' AND '),
130
- *terms.map { |e| [e] * filter.db_column_name.size }.flatten
131
- )
132
- end
133
- records
134
- end
135
-
136
- def new(params)
137
- @ar_object = @ar_model.new
138
- end
139
-
140
- def edit(params)
141
- @ar_object = @ar_model.find(params[:id])
142
- end
143
-
144
- def update(params)
145
- @ar_object = @ar_model.find(params[:id])
146
- @ar_object.update(resource_params(params))
147
- end
148
-
149
- def create(params)
150
- @ar_object = @ar_model.new(resource_params(params))
151
- end
152
-
153
- def resource_params(params)
154
- permittable_fields = @permitted_fields || @ar_model.columns.map(&:name).reject { |i| CmAdmin::REJECTABLE_FIELDS.include?(i) }.map(&:to_sym)
155
- params.require(self.name.underscore.to_sym).permit(*permittable_fields)
156
- end
157
-
158
- def page_title(title)
159
- if @current_action
160
- @current_action.page_title = title
161
- end
162
- end
163
-
164
- def page_description(description)
165
- if @current_action
166
- @current_action.page_description = description
167
- end
168
- end
169
-
170
- def field(field_name, options={})
171
- puts "For printing field #{field_name}"
172
- @available_fields[:show] << CmAdmin::Models::Field.new(field_name, options)
173
- end
174
87
 
175
- def column(field_name, options={})
176
- unless @available_fields[:index].map{|x| x.db_column_name.to_sym}.include?(field_name)
177
- puts "For printing column #{field_name}"
178
- @available_fields[:index] << CmAdmin::Models::Column.new(field_name, options)
179
- end
180
- end
181
-
182
- def all_db_columns(options={})
183
- field_names = self.instance_variable_get(:@ar_model)&.columns&.map{|x| x.name.to_sym}
184
- if options.include?(:exclude) && field_names
185
- excluded_fields = (Array.new << options[:exclude]).flatten.map(&:to_sym)
186
- field_names -= excluded_fields
187
- end
188
- field_names.each do |field_name|
189
- column field_name
190
- end
191
- end
192
-
193
- def self.find_by(search_hash)
194
- CmAdmin.cm_admin_models.find { |x| x.name == search_hash[:name] }
195
- end
196
-
197
- # Custom actions
198
- # eg
199
- # class User < ApplicationRecord
200
- # cm_admin do
201
- # custom_action name: 'submit', verb: 'post', path: ':id/submit' do
202
- # def user_submit
203
- # Code for action here...
204
- # end
205
- # end
206
- # end
207
- # end
208
- def custom_action(name: nil, verb: nil, layout: nil, partial: nil, path: nil, &block)
209
- @available_actions << CmAdmin::Models::Action.new(name: name, verb: verb, layout: layout, partial: partial, path: path)
210
- self.class.class_eval(&block)
211
- end
212
-
213
- def filter(db_column_name, filter_type, options={})
214
- @filters << CmAdmin::Models::Filter.new(db_column_name: db_column_name, filter_type: filter_type, options: options)
215
- end
216
88
  private
217
89
 
218
90
  # Controller defined for each model
@@ -227,7 +99,19 @@ module CmAdmin
227
99
  @model = CmAdmin::Model.find_by(name: controller_name.classify)
228
100
  @model.params = params
229
101
  @action = CmAdmin::Models::Action.find_by(@model, name: action_name)
230
- @ar_object = @model.send(action_name, params)
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
231
115
  respond_to do |format|
232
116
  if %w(show index new edit).include?(action_name)
233
117
  if request.xhr? && action_name.eql?('index')
@@ -242,10 +126,10 @@ module CmAdmin
242
126
  format.html { render '/cm_admin/main/new' }
243
127
  end
244
128
  elsif action.layout.present?
245
- if action.partial.present?
246
- render partial: action.partial, layout: action.layout
129
+ if request.xhr? && action.partial.present?
130
+ format.html { render partial: action.partial }
247
131
  else
248
- render layout: action.layout
132
+ format.html { render action.layout }
249
133
  end
250
134
  end
251
135
  end
@@ -256,7 +140,13 @@ module CmAdmin
256
140
  end
257
141
 
258
142
  def filter_params(params)
259
- params.require(:filters).permit(:search) if params[:filters]
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]
260
150
  end
261
151
  end
262
152
  end
@@ -4,14 +4,28 @@ module CmAdmin
4
4
  module Models
5
5
  class Action
6
6
  include Actions::Blocks
7
- attr_accessor :name, :verb, :layout, :partial, :path, :page_title, :page_description
7
+ attr_accessor :name, :verb, :layout_type, :layout, :partial, :path, :page_title, :page_description, :child_records, :is_nested_field, :nested_table_name
8
8
 
9
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
10
20
  attributes.each do |key, value|
11
21
  self.send("#{key.to_s}=", value)
12
22
  end
13
23
  end
14
24
 
25
+ def set_default_values
26
+ self.is_nested_field = false
27
+ end
28
+
15
29
  class << self
16
30
  def find_by(model, search_hash)
17
31
  model.available_actions.find { |i| i.name == search_hash[:name] }
@@ -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
@@ -1,14 +1,59 @@
1
1
  module CmAdmin
2
2
  module Models
3
3
  class Column
4
- attr_accessor :db_column_name, :column_type, :header, :format, :prefix, :exportable
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
5
6
 
6
- def initialize(db_column_name, attributes = {})
7
- @db_column_name = db_column_name
7
+ def initialize(field_name, attributes = {})
8
+ @field_name = field_name
9
+ set_default_values
8
10
  attributes.each do |key, value|
9
11
  self.send("#{key.to_s}=", value)
10
12
  end
11
- @exportable = true if attributes[:exportable].nil?
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
12
57
  end
13
58
 
14
59
  end