cm-admin 0.7.7 → 0.8.0
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 +4 -4
- data/Gemfile +6 -5
- data/Gemfile.lock +49 -9
- data/app/assets/images/image_not_available.png +0 -0
- data/app/assets/stylesheets/cm_admin/base/table.scss +1 -1
- data/app/assets/stylesheets/cm_admin/cm_admin.css.scss +1 -0
- data/app/assets/stylesheets/cm_admin/pages/import_page.scss +125 -0
- data/app/controllers/cm_admin/resource_controller.rb +26 -4
- data/app/helpers/cm_admin/application_helper.rb +41 -0
- data/app/javascript/packs/cm_admin/exports.js +0 -1
- data/app/jobs/file_import_processor_job.rb +38 -0
- data/app/models/concerns/cm_admin/file_import.rb +40 -0
- data/app/models/file_import.rb +27 -0
- data/app/policies/cm_admin/file_import_policy.rb +11 -0
- data/app/views/cm_admin/main/_actions_dropdown.html.slim +3 -15
- data/app/views/cm_admin/main/_associated_table.html.slim +5 -0
- data/app/views/cm_admin/main/_member_custom_action_modal.html.slim +13 -0
- data/app/views/cm_admin/main/_table.html.slim +3 -1
- data/app/views/cm_admin/main/_tabs.html.slim +1 -1
- data/app/views/cm_admin/main/_top_navbar.html.slim +2 -0
- data/app/views/cm_admin/main/import_form.html.slim +35 -0
- data/cm_admin.gemspec +2 -1
- data/config/routes.rb +6 -0
- data/lib/cm_admin/model.rb +7 -1
- data/lib/cm_admin/models/column.rb +3 -1
- data/lib/cm_admin/models/field.rb +4 -2
- data/lib/cm_admin/models/form_field.rb +3 -2
- data/lib/cm_admin/models/importer.rb +17 -0
- data/lib/cm_admin/version.rb +1 -1
- data/lib/cm_admin/view_helpers/field_display_helper.rb +8 -0
- data/lib/cm_admin/view_helpers/form_field_helper.rb +8 -6
- data/lib/cm_admin/view_helpers.rb +18 -18
- data/lib/cm_admin.rb +2 -1
- data/lib/generators/cm_admin/add_graphql_generator.rb +25 -0
- data/lib/generators/cm_admin/install_generator.rb +2 -0
- data/lib/generators/cm_admin/templates/cm_admin_initializer.rb +1 -1
- data/lib/generators/cm_admin/templates/concerns/attachable.rb +63 -0
- data/lib/generators/cm_admin/templates/concerns/filtered_list.rb +22 -0
- data/lib/generators/cm_admin/templates/concerns/paginator.rb +12 -0
- data/lib/generators/cm_admin/templates/constants.rb +3 -0
- data/lib/generators/cm_admin/templates/exceptions/base_exception.rb +9 -0
- data/lib/generators/cm_admin/templates/graphql/enums/base/sort_column.rb +7 -0
- data/lib/generators/cm_admin/templates/graphql/enums/base/sort_direction.rb +8 -0
- data/lib/generators/cm_admin/templates/graphql/graphql_schema.rb +55 -0
- data/lib/generators/cm_admin/templates/graphql/inputs/base/attachment.rb +15 -0
- data/lib/generators/cm_admin/templates/graphql/inputs/base/filter.rb +15 -0
- data/lib/generators/cm_admin/templates/graphql/inputs/base/paging.rb +15 -0
- data/lib/generators/cm_admin/templates/graphql/inputs/base/sort.rb +15 -0
- data/lib/generators/cm_admin/templates/graphql/mutations/base_mutation.rb +8 -0
- data/lib/generators/cm_admin/templates/graphql/objects/base/attachment_type.rb +31 -0
- data/lib/generators/cm_admin/templates/graphql/objects/base/paging_type.rb +9 -0
- data/lib/generators/cm_admin/templates/graphql/queries/base_query.rb +4 -0
- data/package-lock.json +131 -35
- data/tmp/cache/webpacker/last-compilation-digest-development +1 -1
- data/yarn.lock +5145 -6202
- metadata +47 -7
@@ -0,0 +1,35 @@
|
|
1
|
+
.import-page
|
2
|
+
.import-page__header
|
3
|
+
h1.header-title #{@model.name} Import
|
4
|
+
p.header-description To import #{@model.name.underscore} data using the form below.
|
5
|
+
.import-page__body
|
6
|
+
h6.body-title Import data
|
7
|
+
- if flash[:notice]
|
8
|
+
.success-card
|
9
|
+
h6.success-title
|
10
|
+
i.fa-solid.fa-circle-check
|
11
|
+
| SUCCESS
|
12
|
+
p.success-msg Your file has been uploaded and it will be processed soon.
|
13
|
+
/ p.success-msg-info An email will be sent once the process is completed. If there are any problems with the import, we'll let you know through an email.
|
14
|
+
.actions-wrapper
|
15
|
+
a.secondary-btn href="#{cm_admin.send(:"#{@model.name.underscore}_import_path")}" Import new data
|
16
|
+
/ button.cta-btn.ml-2 Back to Page_Name
|
17
|
+
- else
|
18
|
+
= simple_form_for(FileImport.new, url: "/admin/#{@model.ar_model.table_name}/import", method: :post, html: { class: "csv-import-form" }) do |f|
|
19
|
+
.form-card
|
20
|
+
= f.input :associated_model_name, as: :hidden, placeholder: 'Enter product title', label: false, input_html: { value: @model.name }
|
21
|
+
= f.input :import_file, as: :file, placeholder: 'Enter product title', label: false
|
22
|
+
.note-card
|
23
|
+
h6.note-title
|
24
|
+
i.fa.fa-info-circle
|
25
|
+
| Note
|
26
|
+
.steps-wrapper
|
27
|
+
h6.steps-title Follow these steps to import your data
|
28
|
+
ul.steps-list
|
29
|
+
li
|
30
|
+
| Download this
|
31
|
+
a href="#" template file
|
32
|
+
li Add your data on the file without changing the format
|
33
|
+
li Save the file as a csv, xls or xlsx
|
34
|
+
li Upload the file in the field above
|
35
|
+
= f.button :submit, class: "cta-btn import-btn", value: 'Import data'
|
data/cm_admin.gemspec
CHANGED
@@ -26,11 +26,12 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.bindir = "exe"
|
27
27
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
28
28
|
spec.require_paths = ["lib"]
|
29
|
-
spec.add_runtime_dependency '
|
29
|
+
spec.add_runtime_dependency 'caxlsx_rails'
|
30
30
|
spec.add_runtime_dependency 'cocoon', '~> 1.2.15'
|
31
31
|
spec.add_runtime_dependency 'local_time', '~> 2.1.0'
|
32
32
|
spec.add_runtime_dependency 'pagy', '~> 4.11.0'
|
33
33
|
spec.add_runtime_dependency 'pundit', '~> 2.2.0'
|
34
34
|
spec.add_runtime_dependency 'slim', '~> 4.1.0'
|
35
35
|
spec.add_runtime_dependency 'webpacker', '~> 5.4.3'
|
36
|
+
spec.add_runtime_dependency 'csv-importer', '~> 0.8.2'
|
36
37
|
end
|
data/config/routes.rb
CHANGED
@@ -10,6 +10,12 @@ CmAdmin::Engine.routes.draw do
|
|
10
10
|
|
11
11
|
# Defining action routes for each model
|
12
12
|
CmAdmin.config.cm_admin_models.each do |model|
|
13
|
+
if model.importer
|
14
|
+
scope model.name.tableize do
|
15
|
+
send(:get, 'import', to: "#{model.name.underscore}#import_form", as: "#{model.name.underscore}_import_form")
|
16
|
+
send(:post, 'import', to: "#{model.name.underscore}#import", as: "#{model.name.underscore}_import")
|
17
|
+
end
|
18
|
+
end
|
13
19
|
model.available_actions.sort_by {|act| act.name}.each do |act|
|
14
20
|
scope model.name.tableize do
|
15
21
|
send(act.verb, act.path.present? ? act.path : act.name, to: "#{model.name.underscore}##{act.name}", as: "#{model.name.underscore}_#{act.name}")
|
data/lib/cm_admin/model.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require_relative 'constants'
|
2
2
|
require_relative 'models/action'
|
3
|
+
require_relative 'models/importer'
|
3
4
|
require_relative 'models/custom_action'
|
4
5
|
require_relative 'models/field'
|
5
6
|
require_relative 'models/form_field'
|
@@ -15,6 +16,7 @@ require 'axlsx'
|
|
15
16
|
require 'cocoon'
|
16
17
|
require 'pundit'
|
17
18
|
require 'local_time'
|
19
|
+
require 'csv_importer'
|
18
20
|
|
19
21
|
module CmAdmin
|
20
22
|
class Model
|
@@ -23,7 +25,7 @@ module CmAdmin
|
|
23
25
|
include Models::DslMethod
|
24
26
|
attr_accessor :available_actions, :actions_set, :available_fields, :permitted_fields,
|
25
27
|
:current_action, :params, :filters, :available_tabs, :icon_name
|
26
|
-
attr_reader :name, :ar_model, :is_visible_on_sidebar
|
28
|
+
attr_reader :name, :ar_model, :is_visible_on_sidebar, :importer
|
27
29
|
|
28
30
|
def initialize(entity, &block)
|
29
31
|
@name = entity.name
|
@@ -80,6 +82,10 @@ module CmAdmin
|
|
80
82
|
@actions_set = true
|
81
83
|
end
|
82
84
|
|
85
|
+
def importable(class_name:, importer_type:)
|
86
|
+
@importer = CmAdmin::Models::Importer.new(class_name, importer_type)
|
87
|
+
end
|
88
|
+
|
83
89
|
def visible_on_sidebar(visible_option)
|
84
90
|
@is_visible_on_sidebar = visible_option
|
85
91
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module CmAdmin
|
2
2
|
module Models
|
3
3
|
class Column
|
4
|
-
attr_accessor :field_name, :field_type, :header, :format, :prefix, :suffix, :exportable, :round,
|
4
|
+
attr_accessor :field_name, :field_type, :header, :format, :prefix, :suffix, :exportable, :round, :height, :width,
|
5
5
|
:cm_css_class, :link, :url, :custom_method, :helper_method, :managable, :lockable, :drawer_partial, :tag_class
|
6
6
|
|
7
7
|
def initialize(field_name, attributes = {})
|
@@ -13,6 +13,8 @@ module CmAdmin
|
|
13
13
|
|
14
14
|
#formatting header (either field_name or value present in header attribute)
|
15
15
|
self.send("header=", format_header)
|
16
|
+
self.height = 50 if self.field_type == :image && self.height.nil?
|
17
|
+
self.width = 50 if self.field_type == :image && self.width.nil?
|
16
18
|
end
|
17
19
|
|
18
20
|
#returns a string value as a header (either field_name or value present in header attribute)
|
@@ -2,8 +2,8 @@ module CmAdmin
|
|
2
2
|
module Models
|
3
3
|
class Field
|
4
4
|
|
5
|
-
attr_accessor :field_name, :label, :header, :field_type, :format, :precision,
|
6
|
-
:helper_method, :preview, :custom_link, :precision, :prefix, :suffix, :tag_class
|
5
|
+
attr_accessor :field_name, :label, :header, :field_type, :format, :precision, :height,
|
6
|
+
:width, :helper_method, :preview, :custom_link, :precision, :prefix, :suffix, :tag_class
|
7
7
|
|
8
8
|
def initialize(field_name, attributes = {})
|
9
9
|
@field_name = field_name
|
@@ -11,6 +11,8 @@ module CmAdmin
|
|
11
11
|
attributes.each do |key, value|
|
12
12
|
self.send("#{key.to_s}=", value)
|
13
13
|
end
|
14
|
+
self.height = 50 if self.field_type == :image && self.height.nil?
|
15
|
+
self.width = 50 if self.field_type == :image && self.width.nil?
|
14
16
|
end
|
15
17
|
|
16
18
|
def set_default_values
|
@@ -1,21 +1,22 @@
|
|
1
1
|
module CmAdmin
|
2
2
|
module Models
|
3
3
|
class FormField
|
4
|
-
attr_accessor :field_name, :label, :header, :input_type, :collection, :
|
4
|
+
attr_accessor :field_name, :label, :header, :input_type, :collection, :disabled, :helper_method
|
5
5
|
VALID_INPUT_TYPES = [:integer, :decimal, :string, :single_select, :multi_select, :date, :date_time, :text, :single_file_upload, :multi_file_upload, :hidden, :rich_text].freeze
|
6
6
|
|
7
7
|
def initialize(field_name, input_type, attributes = {})
|
8
|
-
raise ArgumentError, "Kindly select a valid filter type like #{VALID_INPUT_TYPES.sort.to_sentence(last_word_connector: ', or ')} instead of #{input_type} for column #{field_name}" unless VALID_INPUT_TYPES.include?(input_type.to_sym)
|
9
8
|
@field_name = field_name
|
10
9
|
set_default_values
|
11
10
|
attributes.each do |key, value|
|
12
11
|
self.send("#{key.to_s}=", value)
|
13
12
|
end
|
13
|
+
raise ArgumentError, "Kindly select a valid input type like #{VALID_INPUT_TYPES.sort.to_sentence(last_word_connector: ', or ')} instead of #{self.input_type} for form field #{field_name}" unless VALID_INPUT_TYPES.include?(self.input_type.to_sym)
|
14
14
|
end
|
15
15
|
|
16
16
|
def set_default_values
|
17
17
|
self.disabled = false
|
18
18
|
self.label = self.field_name.to_s.titleize
|
19
|
+
self.input_type = :string
|
19
20
|
end
|
20
21
|
end
|
21
22
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module CmAdmin
|
2
|
+
module Models
|
3
|
+
class Importer
|
4
|
+
|
5
|
+
attr_accessor :class_name, :importer_type
|
6
|
+
|
7
|
+
VALID_IMPORTER_TYPES = [:csv_importer, :custom_importer]
|
8
|
+
|
9
|
+
def initialize(class_name, importer_type=:csv_importer)
|
10
|
+
raise ArgumentError, "Kindly select a valid importer type like #{VALID_IMPORTER_TYPES.sort.to_sentence(last_word_connector: ', or ')} instead of #{importer_type}" unless VALID_IMPORTER_TYPES.include?(importer_type.to_sym)
|
11
|
+
@class_name = class_name
|
12
|
+
@importer_type = importer_type
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/cm_admin/version.rb
CHANGED
@@ -61,6 +61,14 @@ module CmAdmin
|
|
61
61
|
concat content_tag(:div, ar_object.send(field.field_name).to_s, class: 'text-ellipsis')
|
62
62
|
concat content_tag(:div, 'View', class: 'drawer-btn')
|
63
63
|
end
|
64
|
+
when :image
|
65
|
+
content_tag(:div, class: 'd-flex') do
|
66
|
+
if ar_object.send(field.field_name).attached?
|
67
|
+
image_tag(ar_object.send(field.field_name).url, height: field.height, width: field.height)
|
68
|
+
else
|
69
|
+
image_tag('/assets/image_not_available', height: 50, width: 50)
|
70
|
+
end
|
71
|
+
end
|
64
72
|
end
|
65
73
|
end
|
66
74
|
|
@@ -2,7 +2,7 @@ module CmAdmin
|
|
2
2
|
module ViewHelpers
|
3
3
|
module FormFieldHelper
|
4
4
|
def input_field_for_column(f, field)
|
5
|
-
value = field.
|
5
|
+
value = field.helper_method ? send(field.helper_method) : f.object.send(field.field_name)
|
6
6
|
is_required = f.object._validators[field.field_name].map(&:kind).include?(:presence)
|
7
7
|
required_class = is_required ? 'required' : ''
|
8
8
|
case field.input_type
|
@@ -13,9 +13,9 @@ module CmAdmin
|
|
13
13
|
when :string
|
14
14
|
return f.text_field field.field_name, class: "normal-input #{required_class}", disabled: field.disabled, value: value, placeholder: "Enter #{field.field_name.to_s.downcase.gsub('_', ' ')}"
|
15
15
|
when :single_select
|
16
|
-
return f.select field.field_name, options_for_select(select_collection_value(field),
|
16
|
+
return f.select field.field_name, options_for_select(select_collection_value(field), f.object.send(field.field_name)), {include_blank: "Select #{field.field_name.to_s.downcase.gsub('_', ' ')}"}, class: "normal-input #{required_class} select-2", disabled: field.disabled
|
17
17
|
when :multi_select
|
18
|
-
return f.select field.field_name, options_for_select(select_collection_value(field),
|
18
|
+
return f.select field.field_name, options_for_select(select_collection_value(field), f.object.send(field.field_name)), {include_blank: "Select #{field.field_name.to_s.downcase.gsub('_', ' ')}"}, class: "normal-input #{required_class} select-2", disabled: field.disabled, multiple: true
|
19
19
|
when :date
|
20
20
|
return f.text_field field.field_name, class: "normal-input #{required_class}", disabled: field.disabled, value: value&.strftime('%d-%m-%Y'), placeholder: "Enter #{field.field_name.to_s.downcase.gsub('_', ' ')}", data: { behaviour: 'date-only' }
|
21
21
|
when :date_time
|
@@ -29,13 +29,15 @@ module CmAdmin
|
|
29
29
|
when :multi_file_upload
|
30
30
|
return f.file_field field.field_name, multiple: true, class: "normal-input #{required_class}"
|
31
31
|
when :hidden
|
32
|
-
return f.hidden_field field.field_name, value:
|
32
|
+
return f.hidden_field field.field_name, value: value
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
+
# Refactor: Collection argument can be removed.
|
37
|
+
# helper_method argument will accept a method where value can be passed.
|
36
38
|
def select_collection_value(field)
|
37
|
-
if field.
|
38
|
-
collection = send(field.
|
39
|
+
if field.helper_method
|
40
|
+
collection = send(field.helper_method)
|
39
41
|
elsif field.collection
|
40
42
|
collection = field.collection
|
41
43
|
else
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module CmAdmin
|
2
2
|
module ViewHelpers
|
3
|
-
Dir[File.expand_path(
|
3
|
+
Dir[File.expand_path('view_helpers', __dir__) + '/*.rb'].each { |f| require f }
|
4
4
|
|
5
5
|
include ActionDropdownHelper
|
6
6
|
include FieldDisplayHelper
|
@@ -14,17 +14,17 @@ module CmAdmin
|
|
14
14
|
include ActionView::Helpers::FormTagHelper
|
15
15
|
include ActionView::Helpers::TagHelper
|
16
16
|
|
17
|
-
def exportable(
|
18
|
-
tag.a
|
17
|
+
def exportable(_klass, html_class: [])
|
18
|
+
tag.a 'Export as excel', class: html_class.append('filter-btn modal-btn mr-2'), data: { toggle: 'modal', target: '#exportmodal' } do
|
19
19
|
concat tag.i class: 'fa fa-download'
|
20
|
-
concat tag.span
|
20
|
+
concat tag.span ' Export'
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
24
|
def column_pop_up(klass, required_filters = nil)
|
25
|
-
tag.div class:
|
26
|
-
tag.div class:
|
27
|
-
tag.div class:
|
25
|
+
tag.div class: 'modal fade form-modal', id: 'exportmodal', role: 'dialog', aria: { labelledby: 'exportModal' } do
|
26
|
+
tag.div class: 'modal-dialog modal-lg', role: 'document' do
|
27
|
+
tag.div class: 'modal-content' do
|
28
28
|
concat pop_ups(klass, required_filters)
|
29
29
|
end
|
30
30
|
end
|
@@ -39,17 +39,17 @@ module CmAdmin
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def pop_up_header
|
42
|
-
tag.div class:
|
43
|
-
tag.button type:
|
44
|
-
tag.span
|
42
|
+
tag.div class: 'modal-header' do
|
43
|
+
tag.button type: 'button', class: 'close', data: { dismiss: 'modal' }, aria: { label: 'Close' } do
|
44
|
+
tag.span 'X', aria: { hidden: 'true' }
|
45
45
|
end
|
46
|
-
tag.h4
|
46
|
+
tag.h4 'Select columns to export', class: 'modal-title', id: 'exportModal'
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
50
|
-
def pop_up_body(klass,
|
51
|
-
tag.div class:
|
52
|
-
form_tag
|
50
|
+
def pop_up_body(klass, _required_filters)
|
51
|
+
tag.div class: 'modal-body' do
|
52
|
+
form_tag cm_admin.send('export_to_file_path'), id: 'export-to-file-form', style: 'width: 100%;', class: 'cm-admin-csv-export-form' do
|
53
53
|
concat hidden_field_tag 'class_name', klass.name.to_s, id: 'export-to-file-klass'
|
54
54
|
concat checkbox_row(klass)
|
55
55
|
concat tag.hr
|
@@ -59,7 +59,7 @@ module CmAdmin
|
|
59
59
|
end
|
60
60
|
|
61
61
|
def checkbox_row(klass)
|
62
|
-
tag.div class:
|
62
|
+
tag.div class: 'row' do
|
63
63
|
CmAdmin::Models::Export.exportable_columns(klass).each do |column_path|
|
64
64
|
concat create_checkbox(column_path)
|
65
65
|
end
|
@@ -67,9 +67,9 @@ module CmAdmin
|
|
67
67
|
end
|
68
68
|
|
69
69
|
def create_checkbox(column_path)
|
70
|
-
tag.div class:
|
71
|
-
concat check_box_tag
|
72
|
-
concat "
|
70
|
+
tag.div class: 'col-md-4' do
|
71
|
+
concat check_box_tag 'columns[]', column_path, id: column_path.to_s.gsub('/', '-')
|
72
|
+
concat " #{column_path.to_s.gsub('/', '_').humanize}"
|
73
73
|
end
|
74
74
|
end
|
75
75
|
end
|
data/lib/cm_admin.rb
CHANGED
@@ -35,7 +35,8 @@ module CmAdmin
|
|
35
35
|
def initialize_model(entity, &block)
|
36
36
|
if entity.is_a?(Class)
|
37
37
|
return if CmAdmin::Model.find_by({name: entity.name})
|
38
|
-
|
38
|
+
cm_model = CmAdmin::Model.new(entity, &block)
|
39
|
+
config.cm_admin_models << cm_model
|
39
40
|
end
|
40
41
|
end
|
41
42
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
|
3
|
+
module CmAdmin
|
4
|
+
module Generators
|
5
|
+
class AddGraphqlGenerator < Rails::Generators::Base
|
6
|
+
source_root File.expand_path('templates', __dir__)
|
7
|
+
|
8
|
+
def add_graphql
|
9
|
+
gem 'graphql'
|
10
|
+
gem 'graphql-errors'
|
11
|
+
gem 'graphql-rails_logger'
|
12
|
+
generate 'graphql:install'
|
13
|
+
template 'graphql/graphql_schema.rb', "app/graphql/#{Rails.application.class.module_parent_name.underscore}_schema.rb"
|
14
|
+
directory 'graphql/inputs/base', 'app/graphql/types/inputs/base'
|
15
|
+
directory 'graphql/enums/base', 'app/graphql/types/enums/base'
|
16
|
+
directory 'graphql/objects/base', 'app/graphql/types/objects/base'
|
17
|
+
directory 'concerns', 'app/models/concerns'
|
18
|
+
copy_file 'graphql/mutations/base_mutation.rb', 'app/graphql/mutations/base_mutation.rb'
|
19
|
+
copy_file 'graphql/queries/base_query.rb', 'app/graphql/queries/base_query.rb'
|
20
|
+
copy_file 'exceptions/base_exception.rb', 'app/exceptions/base_exception.rb'
|
21
|
+
copy_file 'constants.rb', 'config/initializers/constants.rb'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -14,6 +14,8 @@ module CmAdmin
|
|
14
14
|
remove_file 'app/assets/stylesheets/actiontext.scss'
|
15
15
|
copy_file 'application_policy.rb', 'app/policies/application_policy.rb'
|
16
16
|
route 'mount CmAdmin::Engine => "/admin"'
|
17
|
+
generate 'migration', 'CreateFileImport associated_model_name:string added_by:references{polymorphic} error_report:jsonb completed_at:datetime status:integer'
|
18
|
+
rake 'db:migrate'
|
17
19
|
end
|
18
20
|
end
|
19
21
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Attachable
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
included do
|
5
|
+
end
|
6
|
+
|
7
|
+
def save_with_attachments
|
8
|
+
save!
|
9
|
+
save_attachment
|
10
|
+
end
|
11
|
+
|
12
|
+
def update!(args)
|
13
|
+
add_accessors_for_attachment_types
|
14
|
+
super
|
15
|
+
save_attachment
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(args)
|
19
|
+
add_accessors_for_attachment_types
|
20
|
+
super
|
21
|
+
end
|
22
|
+
|
23
|
+
def save_attachment
|
24
|
+
self.class.attachment_types.each do |attachment_type|
|
25
|
+
next if send("#{attachment_type}_file").blank?
|
26
|
+
|
27
|
+
arr = []
|
28
|
+
if send("#{attachment_type}_file").class.eql?(Array)
|
29
|
+
arr = send("#{attachment_type}_file")
|
30
|
+
else
|
31
|
+
arr << send("#{attachment_type}_file")
|
32
|
+
end
|
33
|
+
arr.each do |x|
|
34
|
+
regexp = %r{\Adata:([-\w]+\/[-\w\+\.]+)?;base64,(.*)}m
|
35
|
+
data_uri_parts = x[:content].match(regexp) || []
|
36
|
+
decoded_data = Base64.decode64(data_uri_parts[2])
|
37
|
+
filename = x[:filename]
|
38
|
+
filepath = "#{Rails.root}/tmp/#{filename}"
|
39
|
+
File.open(filepath, 'wb') do |f|
|
40
|
+
f.write(decoded_data)
|
41
|
+
end
|
42
|
+
send(attachment_type.to_s).attach(io: File.open(filepath), filename: filename, content_type: data_uri_parts[1])
|
43
|
+
File.delete(filepath)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def attached_url(attachment_type)
|
49
|
+
if send(attachment_type.to_s).attached? && send(attachment_type.to_s).class == ActiveStorage::Attached::One
|
50
|
+
Rails.application.routes.url_helpers.rails_blob_url(send(attachment_type.to_s))
|
51
|
+
elsif send(attachment_type.to_s).attached? && send(attachment_type.to_s).class == ActiveStorage::Attached::Many
|
52
|
+
send(attachment_type.to_s)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def add_accessors_for_attachment_types
|
59
|
+
self.class.attachment_types.each do |attachment_type|
|
60
|
+
singleton_class.class_eval { attr_accessor "#{attachment_type}_file" }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class FilteredList
|
2
|
+
attr_reader :data, :facets, :paging
|
3
|
+
|
4
|
+
def initialize(paginated_list)
|
5
|
+
self.data = paginated_list[:list]
|
6
|
+
self.paging = paginated_list
|
7
|
+
end
|
8
|
+
|
9
|
+
def data=(data)
|
10
|
+
@data = []
|
11
|
+
@data = data if data.present?
|
12
|
+
end
|
13
|
+
|
14
|
+
def paging=(paginated_list)
|
15
|
+
@paging = {
|
16
|
+
total_items: paginated_list[:list].total_count,
|
17
|
+
current_page: paginated_list[:list].current_page,
|
18
|
+
total_pages: paginated_list[:list].total_pages,
|
19
|
+
total_count: paginated_list[:total_count]
|
20
|
+
}
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Paginator
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
module ClassMethods
|
4
|
+
def list(per_page = DEFAULT_PER_PAGE, page = nil, _filter_params = nil, total_count = nil)
|
5
|
+
paginated_list = {}
|
6
|
+
per_page = DEFAULT_PER_PAGE if per_page == 0
|
7
|
+
paginated_list[:list] = self.page(page || 1).per(per_page)
|
8
|
+
paginated_list[:total_count] = total_count
|
9
|
+
FilteredList.new(paginated_list)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
class <%= Rails.application.class.module_parent_name %>Schema < GraphQL::Schema
|
2
|
+
mutation(Types::MutationType)
|
3
|
+
query(Types::QueryType)
|
4
|
+
|
5
|
+
# Union and Interface Resolution
|
6
|
+
def self.resolve_type(abstract_type, obj, ctx)
|
7
|
+
# TODO: Implement this function
|
8
|
+
# to return the correct object type for `obj`
|
9
|
+
raise(GraphQL::RequiredImplementationMissingError)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Relay-style Object Identification:
|
13
|
+
|
14
|
+
# Return a string UUID for `object`
|
15
|
+
def self.id_from_object(object, type_definition, query_ctx)
|
16
|
+
# Here's a simple implementation which:
|
17
|
+
# - joins the type name & object.id
|
18
|
+
# - encodes it with base64:
|
19
|
+
# GraphQL::Schema::UniqueWithinType.encode(type_definition.name, object.id)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Given a string UUID, find the object
|
23
|
+
def self.object_from_id(id, query_ctx)
|
24
|
+
# For example, to decode the UUIDs generated above:
|
25
|
+
# type_name, item_id = GraphQL::Schema::UniqueWithinType.decode(id)
|
26
|
+
#
|
27
|
+
# Then, based on `type_name` and `id`
|
28
|
+
# find an object in your application
|
29
|
+
# ...
|
30
|
+
end
|
31
|
+
|
32
|
+
rescue_from ActiveRecord::RecordNotFound do |err, obj, args, ctx, field|
|
33
|
+
GraphQL::ExecutionError.new("#{field.type.unwrap.graphql_name} not found", extensions: {code: :unprocessable_entity, sub_code: :record_invalid, message: err.message})
|
34
|
+
end
|
35
|
+
|
36
|
+
rescue_from ActiveRecord::RecordInvalid do |err, obj, args, ctx, field|
|
37
|
+
GraphQL::ExecutionError.new(err.message, extensions: {code: :unprocessable_entity, sub_code: :record_invalid, message: err.message})
|
38
|
+
end
|
39
|
+
|
40
|
+
rescue_from BaseException do |err, obj, args, ctx, field|
|
41
|
+
GraphQL::ExecutionError.new(err.message, extensions: {code: err.code, sub_code: err.sub_code, message: err.message})
|
42
|
+
end
|
43
|
+
|
44
|
+
unless Rails.env.development?
|
45
|
+
rescue_from StandardError do |err, obj, args, ctx, field|
|
46
|
+
rollbar_error = Rollbar.error(err)
|
47
|
+
GraphQL::ExecutionError.new("Internal Server Error", extensions: {code: :internal_server_error, uuid: rollbar_error[:uuid]})
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.unauthorized_object(error)
|
52
|
+
raise Unauthorized, I18n.t("graphql.unauthorized", error_type: error.type.graphql_name)
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Types
|
2
|
+
module Inputs
|
3
|
+
module Base
|
4
|
+
class Attachment < Types::BaseInputObject
|
5
|
+
graphql_name 'AttachmentInput'
|
6
|
+
|
7
|
+
description 'Attributes needed to attach a file'
|
8
|
+
|
9
|
+
argument :filename, String, nil, required: true
|
10
|
+
argument :content, String, nil, required: true
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Types
|
2
|
+
module Inputs
|
3
|
+
module Base
|
4
|
+
class Filter < Types::BaseInputObject
|
5
|
+
graphql_name 'BaseFilterInput'
|
6
|
+
|
7
|
+
description 'Attributes needed for filtering items'
|
8
|
+
|
9
|
+
argument :ids, [Integer], nil, required: false
|
10
|
+
argument :q, String, nil, required: false
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Types
|
2
|
+
module Inputs
|
3
|
+
module Base
|
4
|
+
class Paging < Types::BaseInputObject
|
5
|
+
graphql_name 'PagingInput'
|
6
|
+
|
7
|
+
description 'Attributes needed for paginating list of items'
|
8
|
+
|
9
|
+
argument :page_no, Integer, nil, required: true
|
10
|
+
argument :per_page, Integer, nil, required: false
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Types
|
2
|
+
module Inputs
|
3
|
+
module Base
|
4
|
+
class Sort < Types::BaseInputObject
|
5
|
+
graphql_name 'SortInput'
|
6
|
+
|
7
|
+
description 'Attributes needed for sorting the list of items'
|
8
|
+
|
9
|
+
argument :column, Types::Enums::Base::SortColumn, nil, required: true
|
10
|
+
argument :direction, Types::Enums::Base::SortDirection, nil, required: true
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|