five-two-nw-olivander 0.2.0.1 → 0.2.0.3
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/app/assets/config/olivander_manifest.js +4 -0
- data/app/assets/javascripts/adminlte/datatable.fix.js +42 -0
- data/app/assets/javascripts/adminlte/select2_defaults.coffee +15 -3
- data/app/assets/javascripts/adminlte.js +1 -0
- data/app/assets/javascripts/controllers/input_control_association_controller.js +25 -0
- data/app/components/olivander/components/resource_form_component.html.haml +14 -3
- data/app/components/olivander/components/resource_show_component.html.haml +9 -4
- data/app/controllers/concerns/olivander/resources/application_record.rb +24 -0
- data/app/controllers/concerns/olivander/resources/auto_form_attributes.rb +11 -8
- data/app/controllers/concerns/olivander/resources/crud_controller.rb +39 -0
- data/app/controllers/concerns/olivander/resources/route_builder.rb +5 -4
- data/app/helpers/olivander/application_helper.rb +9 -1
- data/app/inputs/custom_form_builder.rb +41 -0
- data/app/views/application/_form.html.haml +1 -1
- data/app/views/effective/resource/_actions_dropleft.html.haml +16 -10
- data/app/views/layouts/olivander/adminlte/_navbar.html.haml +10 -9
- data/config/initializers/simple_form.rb +57 -10
- data/lib/olivander/application_context.rb +39 -23
- data/lib/olivander/version.rb +1 -1
- metadata +7 -3
- data/app/models/olivander/application_record.rb +0 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2e9d0ccea420078110d4af7de4d7eec3c0ac04e8a8a2533302d5792a1d599c58
|
4
|
+
data.tar.gz: '09b69957d70478ef092e8eff4a979ee867ac74c903fe1572793016d8ab26f531'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 07ce6181e19ada53a8d78cf650c8b4cef82ccb4d38b33a09cca9b340d71567d2a4889f613c5986afb85fb6cc32b0b6010999f9af05fe40ab1ff7ff3a45f291e5
|
7
|
+
data.tar.gz: 67fbcc49d403734676c0f8d46052585ed70e127878bd6f4490f7d94f104d02aa8bcd7827da5d39957182a4a7b9dcc3947b41e69425189844a34e1812684efe15
|
@@ -1 +1,5 @@
|
|
1
1
|
//= link_directory ../stylesheets/olivander .css
|
2
|
+
//= link controllers/datatable_index_charts_controller.js
|
3
|
+
//= link controllers/datatable_expandable_chart_controller.js
|
4
|
+
//= link controllers/turbo_flash_controller.js
|
5
|
+
//= link controllers/input_control_association_controller.js
|
@@ -0,0 +1,42 @@
|
|
1
|
+
(function() {
|
2
|
+
var destroySelect2s, logEvent;
|
3
|
+
|
4
|
+
$.extend(true, $.fn.dataTable.Buttons.defaults, {
|
5
|
+
dom: {
|
6
|
+
button: {
|
7
|
+
className: 'btn btn-outline-primary btn-sm'
|
8
|
+
}
|
9
|
+
}
|
10
|
+
});
|
11
|
+
|
12
|
+
$.extend(true, $.fn.dataTable.ext.classes, {
|
13
|
+
sProcessing: "dataTables_processing card overlay-wrapper"
|
14
|
+
});
|
15
|
+
|
16
|
+
destroySelect2s = function() {
|
17
|
+
$('.effective-datatables-filters').find('select').select2('destroy');
|
18
|
+
return $('.dataTables_wrapper').each(function(_, o) {
|
19
|
+
try {
|
20
|
+
return $(o).find('.dataTables_length select.select2-hidden-accessible').addClass('no-select2').removeAttr('name').select2('destroy');
|
21
|
+
} catch (error) {}
|
22
|
+
});
|
23
|
+
};
|
24
|
+
|
25
|
+
logEvent = function(e) {
|
26
|
+
return console.log(e);
|
27
|
+
};
|
28
|
+
|
29
|
+
$(document).ready(function(e) {
|
30
|
+
return destroySelect2s();
|
31
|
+
});
|
32
|
+
|
33
|
+
// $(document).ready(function(e) {
|
34
|
+
// return $('.effective-datatables-filters input').click(function() {
|
35
|
+
// var $form, $table;
|
36
|
+
// $form = $(event.currentTarget).closest('.effective-datatables-filters');
|
37
|
+
// $table = $('#' + $form.attr('aria-controls'));
|
38
|
+
// return $table.DataTable().draw();
|
39
|
+
// });
|
40
|
+
// });
|
41
|
+
|
42
|
+
}).call(this);
|
@@ -11,13 +11,25 @@ $.fn.select2.defaults.set('dropdownParent', $('#modal-root'))
|
|
11
11
|
#
|
12
12
|
# TODO: Recheck with the select2 GH issue and remove once this is fixed on their side
|
13
13
|
#
|
14
|
-
$(document).on 'select2:open', () =>
|
15
|
-
|
14
|
+
$(document).on 'select2:open', (evt) =>
|
15
|
+
try
|
16
|
+
evt.target.parent.querySelector('.select2-search__field').focus()
|
17
|
+
catch ex
|
18
|
+
evt.target.parentElement.querySelector('.select2-search__field').focus()
|
19
|
+
|
16
20
|
|
17
21
|
initSelect2s = () -> $('select').not('.no-select2').each (k,v) =>
|
18
22
|
$(v).select2({ dropdownParent: $(v).parent() })
|
19
23
|
|
20
|
-
$(document).ready -> initSelect2s()
|
24
|
+
# $(document).ready -> initSelect2s()
|
21
25
|
$(document).on 'show.bs.modal', (e) =>
|
22
26
|
$('select').not('.no-select2').each (k,v) =>
|
23
27
|
$(v).select2({ dropdownParent: $(v).parent() })
|
28
|
+
|
29
|
+
$(document).ready =>
|
30
|
+
$('.effective-datatables-filters').find('select').select2('destroy');
|
31
|
+
$('.dataTables_wrapper').each (_, o) =>
|
32
|
+
try
|
33
|
+
return $(o).find('.dataTables_length select.select2-hidden-accessible').addClass('no-select2').removeAttr('name').select2('destroy');
|
34
|
+
catch error
|
35
|
+
# don't care
|
@@ -0,0 +1,25 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
|
3
|
+
// Connects to data-controller="input-control-association"
|
4
|
+
export default class extends Controller {
|
5
|
+
connect() {
|
6
|
+
var self = this,
|
7
|
+
el = self.element;
|
8
|
+
if (!$(el).hasClass("select2-hidden-accessible")) {
|
9
|
+
$(el).select2({
|
10
|
+
dropdownParent: $(el).parent(),
|
11
|
+
ajax: {
|
12
|
+
url: el.dataset.collectionPath,
|
13
|
+
delay: 250,
|
14
|
+
minimumInputLength: 2,
|
15
|
+
dataType: 'json',
|
16
|
+
processResults: function(data) {
|
17
|
+
return { results: data.map(function(map) {
|
18
|
+
return { id: map.id, text: map.text || map.name || map.description };
|
19
|
+
}) };
|
20
|
+
}
|
21
|
+
}
|
22
|
+
})
|
23
|
+
}
|
24
|
+
}
|
25
|
+
}
|
@@ -5,9 +5,20 @@
|
|
5
5
|
- section.fields.each do |field|
|
6
6
|
%div{ class: section.column_class }
|
7
7
|
- case field.type
|
8
|
-
- when :association
|
9
|
-
= @f.
|
8
|
+
- when :association, :belongs_to_association, :has_many_association, :has_many_through_association, :has_and_belongs_to_many_reflection, :has_one_through_association
|
9
|
+
- reflection = @f.object.class.reflect_on_association(field.sym)
|
10
|
+
- controllers = "#{field.type}-#{@resource.class.name.underscore.gsub('/', '_')}-#{field.sym} input-control-association"
|
11
|
+
- begin
|
12
|
+
- collection_path = polymorphic_path(@resource.class.reflect_on_association(field.sym).klass, format: :json)
|
13
|
+
- rescue
|
14
|
+
- collection_path = ''
|
15
|
+
- collection = @f.object.send(field.sym)
|
16
|
+
- case field.type
|
17
|
+
- when :has_one_through_association
|
18
|
+
= @f.input field.sym, selected: collection, collection: [collection].flatten, disabled: !field.editable, input_html: { data: { collection_path: collection_path, controller: controllers } }
|
19
|
+
-else
|
20
|
+
= @f.association field.sym, collection: [collection].flatten, disabled: !field.editable, input_html: { data: { collection_path: collection_path, controller: controllers } }
|
10
21
|
- when :boolean
|
11
22
|
= @f.input field.sym, disabled: !field.editable, as: field.type.to_sym, input_html: { data: { controller: "input-control-#{field.type}" } }, wrapper: :checkbox
|
12
23
|
- else
|
13
|
-
= @f.input field.sym, disabled: !field.editable, as: field.type.to_sym, input_html: { data: { controller: "input-control-#{field.type}" } }
|
24
|
+
= @f.input field.sym, disabled: !field.editable, as: field.type.to_sym, input_html: { rows: 10, data: { controller: "input-control-#{field.type}" } }
|
@@ -23,14 +23,20 @@
|
|
23
23
|
- when :boolean
|
24
24
|
- icon_class = val ? 'fa-check text-success' : 'fa-times text-danger'
|
25
25
|
%i.fa{ class: icon_class }
|
26
|
-
- when :association
|
26
|
+
- when :association, :belongs_to_association, :has_many_association, :has_many_through_association, :has_and_belongs_to_many_reflection, :has_one_through_association
|
27
27
|
- if val.present?
|
28
28
|
- if val.is_a?(ActiveRecord::Associations::CollectionProxy)
|
29
29
|
%ul
|
30
30
|
- val.each do |val1|
|
31
|
-
|
31
|
+
- begin
|
32
|
+
%li= link_to val1
|
33
|
+
- rescue
|
34
|
+
%li= val1
|
32
35
|
- else
|
33
|
-
|
36
|
+
- begin
|
37
|
+
= link_to val if val.present?
|
38
|
+
- rescue
|
39
|
+
= val if val.present?
|
34
40
|
- when :file
|
35
41
|
- if val.present?
|
36
42
|
- if val.content_type.include?('image')
|
@@ -38,7 +44,6 @@
|
|
38
44
|
- else
|
39
45
|
= link_to val, val.expiring_url
|
40
46
|
- else
|
41
|
-
= f.type
|
42
47
|
= val
|
43
48
|
- (section.columns-slice.size).times do
|
44
49
|
%th
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Olivander
|
2
|
+
module Resources
|
3
|
+
module ApplicationRecord
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
def self.audited_as klazz
|
8
|
+
# Rails.logger.debug "#{self.class.name} is audited as #{klazz.name}"
|
9
|
+
@@audited_user_class = klazz
|
10
|
+
|
11
|
+
belongs_to :created_by, class_name: klazz.name
|
12
|
+
belongs_to :updated_by, class_name: klazz.name
|
13
|
+
|
14
|
+
before_validation :set_audit_user
|
15
|
+
end
|
16
|
+
|
17
|
+
def set_audit_user
|
18
|
+
self.created_by ||= @@audited_user_class.current
|
19
|
+
self.updated_by = @@audited_user_class.current
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -33,16 +33,16 @@ module Olivander
|
|
33
33
|
self.resource_field_group_collection
|
34
34
|
end
|
35
35
|
|
36
|
-
def self.auto_resource_fields(columns: 2, only: [])
|
36
|
+
def self.auto_resource_fields(columns: 2, only: [], except: [], editable: true)
|
37
37
|
return unless ActiveRecord::Base.connection.table_exists?(table_name)
|
38
38
|
|
39
39
|
if current_resource_field_group.nil?
|
40
40
|
resource_field_group do
|
41
|
-
auto_resource_fields(columns: columns, only: only)
|
41
|
+
auto_resource_fields(columns: columns, only: only, except: except, editable: editable)
|
42
42
|
end
|
43
43
|
elsif current_resource_field_group.forced_section.nil?
|
44
44
|
resource_field_section(columns) do
|
45
|
-
auto_resource_fields(columns: columns, only: only)
|
45
|
+
auto_resource_fields(columns: columns, only: only, except: except, editable: editable)
|
46
46
|
end
|
47
47
|
else
|
48
48
|
if only.size.zero?
|
@@ -53,27 +53,30 @@ module Olivander
|
|
53
53
|
only << attachment_definitions.select{ |x| x[0] } if respond_to?(:attachment_definitions)
|
54
54
|
only = only.flatten - SKIPPED_ATTRIBUTES
|
55
55
|
end
|
56
|
+
only = only - except
|
56
57
|
only.each do |inc|
|
57
58
|
self.columns.each do |att|
|
58
59
|
sym = att.name.to_sym
|
59
60
|
type = att.type
|
60
61
|
next unless inc == sym
|
61
62
|
|
62
|
-
resource_field sym, type
|
63
|
+
resource_field sym, type, editable: editable
|
63
64
|
end
|
64
65
|
|
65
66
|
reflections.map{ |x| x[1] }
|
66
67
|
.filter{ |x| x.foreign_key == inc || x.name == inc }
|
67
68
|
.each do |r|
|
68
|
-
|
69
|
-
|
70
|
-
|
69
|
+
begin
|
70
|
+
resource_field(r.name, r.association_class.name.demodulize.underscore.to_sym, editable: editable)
|
71
|
+
rescue NotImplementedError
|
72
|
+
resource_field(r.name, :association, editable: editable)
|
73
|
+
end
|
71
74
|
end
|
72
75
|
|
73
76
|
next unless respond_to?(:attachment_definitions)
|
74
77
|
|
75
78
|
attachment_definitions.filter{ |x| x == inc }.each do |ad|
|
76
|
-
resource_field ad[0], :file
|
79
|
+
resource_field ad[0], :file, editable: editable
|
77
80
|
end
|
78
81
|
end
|
79
82
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Olivander
|
2
|
+
module Resources
|
3
|
+
module CrudController
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
include Effective::CrudController
|
8
|
+
layout 'olivander/adminlte/main'
|
9
|
+
|
10
|
+
def index
|
11
|
+
if request.format == :json && params[:_type].present? && params[:_type] == 'query'
|
12
|
+
index_search
|
13
|
+
else
|
14
|
+
super
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def index_search
|
19
|
+
self.resources ||= resource_scope.all if resource_scope.respond_to?(:all)
|
20
|
+
if resource_scope.respond_to?(:search_for)
|
21
|
+
self.resources = self.resources.search_for(params[:term])
|
22
|
+
else
|
23
|
+
k = resources.klass
|
24
|
+
like_term = "%#{ActiveRecord::Base.sanitize_sql_like(params[:term])}%"
|
25
|
+
fields = %w[name title description text].keep_if{ |field| k.respond_to?(field) }
|
26
|
+
clauses = fields.map{ |field| "#{field} ilike '#{like_term}'" }.join(' or ')
|
27
|
+
orders = fields.join(', ')
|
28
|
+
self.resources = self.resources.where(clauses).order(orders) if clauses.length.positive?
|
29
|
+
end
|
30
|
+
self.resources = self.resources.limit(25)
|
31
|
+
end
|
32
|
+
|
33
|
+
def permitted_params
|
34
|
+
params.fetch(resource_klass.name.underscore.gsub('/', '_').to_sym, {}).permit!
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -3,12 +3,12 @@ module Olivander
|
|
3
3
|
class ResourceAction
|
4
4
|
attr_accessor :sym, :action, :verb, :confirm, :turbo_frame, :collection,
|
5
5
|
:controller, :crud_action, :show_in_form, :show_in_datatable,
|
6
|
-
:no_route, :path_helper, :confirm_with
|
6
|
+
:no_route, :path_helper, :confirm_with, :primary
|
7
7
|
|
8
8
|
def initialize(sym, action: nil, controller: nil, verb: :get, confirm: false,
|
9
9
|
turbo_frame: nil, collection: false, crud_action: false,
|
10
10
|
show_in_form: true, show_in_datatable: true, no_route: false,
|
11
|
-
path_helper: nil, confirm_with: nil)
|
11
|
+
path_helper: nil, confirm_with: nil, primary: nil)
|
12
12
|
self.sym = sym
|
13
13
|
self.action = action || sym
|
14
14
|
self.controller = controller
|
@@ -22,6 +22,7 @@ module Olivander
|
|
22
22
|
self.no_route = no_route
|
23
23
|
self.path_helper = path_helper
|
24
24
|
self.confirm_with = confirm_with
|
25
|
+
self.primary = primary || crud_action
|
25
26
|
end
|
26
27
|
|
27
28
|
def args_hash(options = nil)
|
@@ -131,7 +132,7 @@ module Olivander
|
|
131
132
|
|
132
133
|
def action(sym, verb: :get, confirm: false, turbo_frame: nil, collection: false, show_in_datatable: true,
|
133
134
|
show_in_form: true, no_route: false, controller: nil, action: nil, path_helper: nil,
|
134
|
-
confirm_with: nil
|
135
|
+
confirm_with: nil, primary: nil
|
135
136
|
)
|
136
137
|
raise 'Must be invoked in a resource block' unless current_resource.present?
|
137
138
|
|
@@ -139,7 +140,7 @@ module Olivander
|
|
139
140
|
current_resource.actions << ResourceAction.new(
|
140
141
|
sym, action: action, controller: controller, verb: verb, confirm: confirm, turbo_frame: turbo_frame, collection: collection,
|
141
142
|
show_in_datatable: show_in_datatable, show_in_form: show_in_form, no_route: no_route, path_helper: path_helper,
|
142
|
-
confirm_with: confirm_with
|
143
|
+
confirm_with: confirm_with, primary: primary
|
143
144
|
)
|
144
145
|
end
|
145
146
|
|
@@ -73,6 +73,14 @@ module Olivander
|
|
73
73
|
render partial: 'resource_form_actions', locals: { actions: authorized_resource_actions(resource, for_action: for_action).select(&:show_in_form) }
|
74
74
|
end
|
75
75
|
|
76
|
+
def resource_form_action_tooltip(resource, action)
|
77
|
+
key = resource.class.name.underscore
|
78
|
+
return I18n.t("activerecord.actions.#{key}.#{action}-tooltip") if I18n.exists?("activerecord.actions.#{key}.#{action}-tooltip")
|
79
|
+
return I18n.t("activerecord.actions.#{action}-tooltip") if I18n.exists?("activerecord.actions.#{action}-tooltip")
|
80
|
+
|
81
|
+
action.to_s.titleize
|
82
|
+
end
|
83
|
+
|
76
84
|
def resource_form_action_label(resource, action)
|
77
85
|
key = resource.class.name.underscore
|
78
86
|
return I18n.t("activerecord.actions.#{key}.#{action}") if I18n.exists?("activerecord.actions.#{key}.#{action}")
|
@@ -95,7 +103,7 @@ module Olivander
|
|
95
103
|
end
|
96
104
|
|
97
105
|
def current_user
|
98
|
-
Olivander::CurrentContext.user
|
106
|
+
Olivander::CurrentContext.user
|
99
107
|
end
|
100
108
|
|
101
109
|
def current_ability
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class CustomFormBuilder < SimpleForm::FormBuilder
|
2
|
+
def initialize(*)
|
3
|
+
super
|
4
|
+
end
|
5
|
+
|
6
|
+
def association(association, options = {}, &block)
|
7
|
+
resolve_custom_input_association(association, options)
|
8
|
+
super(association, options, &block)
|
9
|
+
end
|
10
|
+
|
11
|
+
def resolve_custom_input_association(association, options)
|
12
|
+
return if options[:as].present?
|
13
|
+
|
14
|
+
[
|
15
|
+
"#{object.class.name.demodulize.underscore}_#{association.to_s}".to_sym, association
|
16
|
+
].each do |key|
|
17
|
+
camelized = "#{key.to_s.camelize}Input"
|
18
|
+
mapping = attempt_mapping_with_custom_namespace(camelized) ||
|
19
|
+
attempt_mapping(camelized, Object) ||
|
20
|
+
attempt_mapping(camelized, self.class)
|
21
|
+
|
22
|
+
next unless mapping.present?
|
23
|
+
|
24
|
+
options[:as] = key
|
25
|
+
break
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def fetch_association_collection(reflection, options)
|
32
|
+
options_method = "options_for_#{reflection.name}".to_sym
|
33
|
+
if object.respond_to?(options_method) then
|
34
|
+
options.fetch(:collection) do
|
35
|
+
object.send(options_method)
|
36
|
+
end
|
37
|
+
else
|
38
|
+
super(reflection, options)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
- read_only = !%w[new create edit update].include?(action_name)
|
2
|
-
= simple_form_for(@resource) do |f|
|
2
|
+
= simple_form_for(@resource, builder: CustomFormBuilder) do |f|
|
3
3
|
.card.card-primary
|
4
4
|
.card-header
|
5
5
|
%h3.card-title= @resource.to_s.blank? ? @resource.model_name.human : @resource
|
@@ -4,21 +4,27 @@
|
|
4
4
|
-# - data = a.turbo_frame.present? ? { turbo: true, turbo_frame: a.turbo_frame } : {}
|
5
5
|
-# = dropdown_link_to resource_form_action_label(resource, a.sym), path, data: data
|
6
6
|
- actions = authorized_resource_actions(resource, for_action: action_name).select{ |x| x.show_in_datatable }
|
7
|
-
-
|
8
|
-
-
|
7
|
+
- primary_actions = actions.select{ |a| a.primary }
|
8
|
+
- non_primary_actions = actions.select{ |a| !a.primary }
|
9
9
|
.btn-group
|
10
|
-
- if
|
10
|
+
- if non_primary_actions.size.positive?
|
11
11
|
.btn-group
|
12
12
|
%button.btn.btn-sm.dropdown-toggle{ type: :button, data: { toggle: 'dropdown' }}
|
13
13
|
%ul.dropdown-menu
|
14
|
-
-
|
14
|
+
- non_primary_actions.each do |a|
|
15
15
|
- path = a.path_helper.is_a?(Proc) ? a.path_helper.call(resource) : send(a.path_helper, resource.id)
|
16
|
-
-
|
16
|
+
- hash = {}
|
17
|
+
- data = a.turbo_frame.present? ? { turbo: true, turbo_frame: a.turbo_frame, turbo_method: a.verb } : {}
|
18
|
+
- hash[:data] = data
|
19
|
+
- hash[:method] = a.verb unless a.turbo_frame.present?
|
17
20
|
%li
|
18
|
-
= dropdown_link_to resource_form_action_label(resource, a.sym), path,
|
19
|
-
-
|
21
|
+
= dropdown_link_to resource_form_action_label(resource, a.sym), path, hash
|
22
|
+
- primary_actions.each do |a|
|
20
23
|
- path = a.path_helper.is_a?(Proc) ? a.path_helper.call(resource) : send(a.path_helper, resource.id)
|
21
24
|
- icon_class = (a.verb == :delete ? '' : '')
|
22
|
-
= link_to path, a.args_hash(title:
|
23
|
-
|
24
|
-
|
25
|
+
= link_to path, a.args_hash(title: resource_form_action_tooltip(resource, a.sym), class: "btn btn-sm #{icon_class}") do
|
26
|
+
- action_label = resource_form_action_label(resource, a.sym)
|
27
|
+
- icon_font_size = action_label.blank? ? '18px' : '10px'
|
28
|
+
%i{ class: resource_form_action_icon(resource, a.sym), style: "display: block; font-size: #{icon_font_size}" }
|
29
|
+
- unless action_label.blank?
|
30
|
+
%span{ style: 'font-size: 12px' }= action_label
|
@@ -101,20 +101,21 @@
|
|
101
101
|
-# %a.dropdown-item.dropdown-footer{:href => "#"} See All Notifications
|
102
102
|
%li.nav-item.dropdown.user-menu
|
103
103
|
%a.nav-link.dropdown-toggle{"data-toggle" => "dropdown", :href => "#"}
|
104
|
-
%img.user-image.img-circle.elevation-2{:alt => "User Image", :src => image_path(user_image_path(
|
104
|
+
%img.user-image.img-circle.elevation-2{:alt => "User Image", :src => image_path(user_image_path(Olivander::CurrentContext.user))}/
|
105
105
|
|
106
|
-
%span.d-none.d-md-inline=
|
106
|
+
%span.d-none.d-md-inline= Olivander::CurrentContext.user.display_name
|
107
107
|
%ul.dropdown-menu.dropdown-menu-lg.dropdown-menu-right
|
108
108
|
/ User image
|
109
109
|
%li.user-header.bg-primary
|
110
|
-
%img.img-circle.elevation-2{:alt => "User Image", src: image_path(user_image_path(
|
111
|
-
%p=
|
110
|
+
%img.img-circle.elevation-2{:alt => "User Image", src: image_path(user_image_path(Olivander::CurrentContext.user))}/
|
111
|
+
%p= Olivander::CurrentContext.user.display_name
|
112
112
|
/ Menu Footer
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
113
|
+
- unless Olivander::CurrentContext.user.is_a?(OpenStruct)
|
114
|
+
%li.user-footer
|
115
|
+
= link_to polymorphic_path(Olivander::CurrentContext.user), class: 'btn btn-default btn-flat' do
|
116
|
+
Profile
|
117
|
+
= link_to Olivander::CurrentContext.application_context.sign_out_path, method: :delete, class: 'btn btn-default btn-flat float-right' do
|
118
|
+
Sign out
|
118
119
|
%li.nav-item
|
119
120
|
%a.nav-link{title: 'Full Screen Mode', "data-widget" => "fullscreen", :href => "#", :role => "button"}
|
120
121
|
%i.fas.fa-expand-arrows-alt
|
@@ -70,16 +70,49 @@ SimpleForm.setup do |config|
|
|
70
70
|
config.wrappers :checkbox, class: "form-check",
|
71
71
|
hint_class: :field_with_hint, error_class: :field_with_errors, valid_class: :field_without_errors do |b|
|
72
72
|
b.use :html5
|
73
|
-
b.use :placeholder
|
74
|
-
b.optional :maxlength
|
75
|
-
b.optional :minlength
|
76
|
-
b.optional :pattern
|
77
|
-
b.optional :min_max
|
78
73
|
b.optional :readonly
|
79
|
-
b.
|
80
|
-
|
81
|
-
|
82
|
-
b.use :
|
74
|
+
b.wrapper :legend_tag, tag: 'legend', class: 'col-form-label pt-0' do |ba|
|
75
|
+
ba.use :label_text
|
76
|
+
end
|
77
|
+
b.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
|
78
|
+
b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
|
79
|
+
b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
|
80
|
+
end
|
81
|
+
|
82
|
+
# vertical input for boolean
|
83
|
+
config.wrappers :vertical_boolean, tag: 'fieldset', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
|
84
|
+
b.use :html5
|
85
|
+
b.optional :readonly
|
86
|
+
b.wrapper :form_check_wrapper, tag: 'div', class: 'form-check' do |bb|
|
87
|
+
bb.use :input, class: 'form-check-input' #, error_class: 'is-invalid', valid_class: 'is-valid'
|
88
|
+
bb.use :label, class: 'form-check-label'
|
89
|
+
bb.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
|
90
|
+
bb.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# vertical input for radio buttons and check boxes
|
95
|
+
config.wrappers :vertical_collection, item_wrapper_class: 'form-check', item_label_class: 'form-check-label', tag: 'fieldset', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
|
96
|
+
b.use :html5
|
97
|
+
b.optional :readonly
|
98
|
+
b.wrapper :legend_tag, tag: 'legend', class: 'col-form-label pt-0' do |ba|
|
99
|
+
ba.use :label_text
|
100
|
+
end
|
101
|
+
b.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
|
102
|
+
b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
|
103
|
+
b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
|
104
|
+
end
|
105
|
+
|
106
|
+
# vertical input for inline radio buttons and check boxes
|
107
|
+
config.wrappers :vertical_collection_inline, item_wrapper_class: 'form-check form-check-inline', item_label_class: 'form-check-label', tag: 'fieldset', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
|
108
|
+
b.use :html5
|
109
|
+
b.optional :readonly
|
110
|
+
b.wrapper :legend_tag, tag: 'legend', class: 'col-form-label pt-0' do |ba|
|
111
|
+
ba.use :label_text
|
112
|
+
end
|
113
|
+
b.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
|
114
|
+
b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
|
115
|
+
b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
|
83
116
|
end
|
84
117
|
|
85
118
|
config.wrappers :switch, class: "custom-control custom-switch",
|
@@ -170,10 +203,24 @@ SimpleForm.setup do |config|
|
|
170
203
|
# Custom wrappers for input types. This should be a hash containing an input
|
171
204
|
# type as key and the wrapper that will be used for all inputs with specified type.
|
172
205
|
# config.wrapper_mappings = { string: :prepend }
|
206
|
+
config.wrapper_mappings = {
|
207
|
+
boolean: :vertical_boolean,
|
208
|
+
check_boxes: :check_box,
|
209
|
+
# date: :horizontal_multi_select,
|
210
|
+
# datetime: :horizontal_multi_select,
|
211
|
+
# file: :horizontal_file,
|
212
|
+
# radio_buttons: :horizontal_collection,
|
213
|
+
# range: :horizontal_range,
|
214
|
+
# time: :horizontal_multi_select
|
215
|
+
}
|
173
216
|
|
174
217
|
# Namespaces where SimpleForm should look for custom input classes that
|
175
218
|
# override default inputs.
|
176
|
-
|
219
|
+
Dir.glob('app/inputs/**/')
|
220
|
+
.map{ |x| x.gsub('app/inputs/', '').split('/').reject(&:blank?).join('/').camelize }
|
221
|
+
.reject(&:blank?).each do |d|
|
222
|
+
config.custom_inputs_namespaces << d
|
223
|
+
end
|
177
224
|
|
178
225
|
# Default priority for time_zone inputs.
|
179
226
|
# config.time_zone_priority = nil
|
@@ -2,23 +2,17 @@ module Olivander
|
|
2
2
|
class ApplicationContext
|
3
3
|
attr_accessor :name, :logo, :company, :menu_items, :route_builder, :sign_out_path, :sidebar_background_class
|
4
4
|
|
5
|
-
def
|
6
|
-
|
7
|
-
|
5
|
+
def initialize(**kwargs)
|
6
|
+
self.name = kwargs[:name] || ENV['OLIVANDER_APP_NAME'] || 'Application Name'
|
7
|
+
self.logo = kwargs[:logo] || Logo.new(url: kwargs[:logo_url], alt: kwargs[:logo_alt])
|
8
|
+
self.company = kwargs[:company] || Company.new(name: kwargs[:company_name], url: kwargs[:company_url])
|
9
|
+
self.sign_out_path = kwargs[:sign_out_path] || '/sign_out'
|
10
|
+
self.menu_items = kwargs[:menu_items] || []
|
8
11
|
begin
|
9
|
-
|
10
|
-
rescue
|
11
|
-
|
12
|
+
self.route_builder = RouteBuilder.new
|
13
|
+
rescue NameError
|
14
|
+
self.route_builder = OpenStruct.new(resources: {})
|
12
15
|
end
|
13
|
-
ctx
|
14
|
-
end
|
15
|
-
|
16
|
-
def initialize(name: 'Application Name', logo: Logo.new, company: Company.new, sign_out_path: '/sign_out', menu_items: [])
|
17
|
-
self.name = name
|
18
|
-
self.logo = logo
|
19
|
-
self.company = company
|
20
|
-
self.sign_out_path = sign_out_path
|
21
|
-
self.menu_items = menu_items
|
22
16
|
end
|
23
17
|
|
24
18
|
def visible_modules
|
@@ -41,18 +35,18 @@ module Olivander
|
|
41
35
|
class Logo
|
42
36
|
attr_accessor :url, :alt
|
43
37
|
|
44
|
-
def initialize(
|
45
|
-
self.url = url
|
46
|
-
self.alt = alt
|
38
|
+
def initialize(**kwargs)
|
39
|
+
self.url = kwargs[:url] || ENV['OLIVANDER_LOGO_URL'] || '/images/olivander_logo.png'
|
40
|
+
self.alt = kwargs[:alt] || ENV['OLIVANDER_LOGO_ALT'] || 'Logo Image'
|
47
41
|
end
|
48
42
|
end
|
49
43
|
|
50
44
|
class Company
|
51
45
|
attr_accessor :name, :url
|
52
46
|
|
53
|
-
def initialize(
|
54
|
-
self.url = url
|
55
|
-
self.name = name
|
47
|
+
def initialize(**kwargs)
|
48
|
+
self.url = kwargs[:url] || ENV['OLIVANDER_COMPANY_URL'] || '/'
|
49
|
+
self.name = kwargs[:name] || ENV['OLIVANDER_COMPANY_NAME'] || 'Company Name'
|
56
50
|
end
|
57
51
|
end
|
58
52
|
end
|
@@ -61,8 +55,30 @@ module Olivander
|
|
61
55
|
attribute :application_context
|
62
56
|
attribute :user, :ability
|
63
57
|
|
64
|
-
def
|
65
|
-
|
58
|
+
def build(&block)
|
59
|
+
self.application_context ||= ::Olivander::ApplicationContext.new
|
60
|
+
self.user ||= build_dummy_user
|
61
|
+
self.ability ||= build_dummy_ability
|
62
|
+
yield(self, application_context, user, ability) if block_given?
|
63
|
+
self
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def build_dummy_user
|
69
|
+
OpenStruct.new({ display_name: 'No User Set' })
|
70
|
+
end
|
71
|
+
|
72
|
+
def build_dummy_ability
|
73
|
+
Class.new do
|
74
|
+
def can?(_action, _resource)
|
75
|
+
true
|
76
|
+
end
|
77
|
+
|
78
|
+
def authorize!(_action, _resource)
|
79
|
+
true
|
80
|
+
end
|
81
|
+
end.new
|
66
82
|
end
|
67
83
|
end
|
68
84
|
end
|
data/lib/olivander/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: five-two-nw-olivander
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.0.
|
4
|
+
version: 0.2.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eric Dennis
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-02-
|
11
|
+
date: 2024-02-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: chartkick
|
@@ -175,6 +175,7 @@ files:
|
|
175
175
|
- app/assets/images/default-150x150.png
|
176
176
|
- app/assets/images/icons.png
|
177
177
|
- app/assets/javascripts/adminlte.js
|
178
|
+
- app/assets/javascripts/adminlte/datatable.fix.js
|
178
179
|
- app/assets/javascripts/adminlte/dist/css/adminlte.min.css
|
179
180
|
- app/assets/javascripts/adminlte/dist/js/adminlte.js
|
180
181
|
- app/assets/javascripts/adminlte/plugins/bootstrap/js/bootstrap.bundle.min.js
|
@@ -208,6 +209,7 @@ files:
|
|
208
209
|
- app/assets/javascripts/adminlte/select2_defaults.coffee
|
209
210
|
- app/assets/javascripts/controllers/datatable_expandable_chart_controller.js
|
210
211
|
- app/assets/javascripts/controllers/datatable_index_charts_controller.js
|
212
|
+
- app/assets/javascripts/controllers/input_control_association_controller.js
|
211
213
|
- app/assets/javascripts/controllers/turbo_flash_controller.js
|
212
214
|
- app/assets/javascripts/olivander/flash_toast.js
|
213
215
|
- app/assets/stylesheets/adminlte.css
|
@@ -228,15 +230,17 @@ files:
|
|
228
230
|
- app/components/olivander/components/table_portlet_component.html.haml
|
229
231
|
- app/components/olivander/components/table_portlet_component.rb
|
230
232
|
- app/components/olivander/components/table_portlet_component/table_component.html.haml
|
233
|
+
- app/controllers/concerns/olivander/resources/application_record.rb
|
231
234
|
- app/controllers/concerns/olivander/resources/auto_form_attributes.rb
|
235
|
+
- app/controllers/concerns/olivander/resources/crud_controller.rb
|
232
236
|
- app/controllers/concerns/olivander/resources/route_builder.rb
|
233
237
|
- app/controllers/olivander/application_controller.rb
|
234
238
|
- app/datatables/olivander/datatable.rb
|
235
239
|
- app/helpers/olivander/application_helper.rb
|
240
|
+
- app/inputs/custom_form_builder.rb
|
236
241
|
- app/inputs/date_time_input.rb
|
237
242
|
- app/jobs/olivander/application_job.rb
|
238
243
|
- app/mailers/olivander/application_mailer.rb
|
239
|
-
- app/models/olivander/application_record.rb
|
240
244
|
- app/views/application/_form.html.haml
|
241
245
|
- app/views/application/_resource_form_actions.html.haml
|
242
246
|
- app/views/application/edit.html.haml
|
@@ -1,20 +0,0 @@
|
|
1
|
-
module Olivander
|
2
|
-
class ApplicationRecord < ActiveRecord::Base
|
3
|
-
self.abstract_class = true
|
4
|
-
|
5
|
-
def self.audited_as klazz
|
6
|
-
# Rails.logger.debug "#{self.class.name} is audited as #{klazz.name}"
|
7
|
-
@@audited_user_class = klazz
|
8
|
-
|
9
|
-
belongs_to :created_by, class_name: klazz.name
|
10
|
-
belongs_to :updated_by, class_name: klazz.name
|
11
|
-
|
12
|
-
before_validation :set_audit_user
|
13
|
-
end
|
14
|
-
|
15
|
-
def set_audit_user
|
16
|
-
self.created_by ||= @@audited_user_class.current
|
17
|
-
self.updated_by = @@audited_user_class.current
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|