cm-admin 0.7.7 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|