base-project 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +37 -0
  3. data/.travis.yml +5 -0
  4. data/CODE_OF_CONDUCT.md +74 -0
  5. data/Gemfile +6 -0
  6. data/Gemfile.lock +196 -0
  7. data/README.md +43 -0
  8. data/Rakefile +6 -0
  9. data/base-project.gemspec +38 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/lib/base/project.rb +7 -0
  13. data/lib/base/project/app/controllers/concerns/base_crud_controller.rb +104 -0
  14. data/lib/base/project/app/helpers/base_helper.rb +128 -0
  15. data/lib/base/project/app/helpers/button_helper.rb +101 -0
  16. data/lib/base/project/app/helpers/localizable_helper.rb +51 -0
  17. data/lib/base/project/app/helpers/panel_helper.rb +8 -0
  18. data/lib/base/project/app/models/concerns/addressable.rb +93 -0
  19. data/lib/base/project/app/models/concerns/base_user_concern.rb +39 -0
  20. data/lib/base/project/app/models/concerns/image_concern.rb +16 -0
  21. data/lib/base/project/app/models/concerns/localizable.rb +50 -0
  22. data/lib/base/project/app/models/concerns/scope_concern.rb +14 -0
  23. data/lib/base/project/app/validators/image_validator.rb +14 -0
  24. data/lib/base/project/app/views/_flash_messages.html.erb +12 -0
  25. data/lib/base/project/app/views/_menu.html.erb +16 -0
  26. data/lib/base/project/app/views/forms/_buttons.html.erb +4 -0
  27. data/lib/base/project/app/views/indexes/_buttons.html.erb +11 -0
  28. data/lib/base/project/app/views/indexes/_header.html.erb +10 -0
  29. data/lib/base/project/app/views/menu/_header_menu.html.erb +25 -0
  30. data/lib/base/project/app/views/menu/_sidebar.html.erb +44 -0
  31. data/lib/base/project/app/views/panels/_panel.html.erb +15 -0
  32. data/lib/base/project/app/views/versions/_model_dates.html.erb +12 -0
  33. data/lib/base/project/config/locales/error_pages.pt-BR.yml +9 -0
  34. data/lib/base/project/config/locales/pt-BR.yml +269 -0
  35. data/lib/base/project/config/locales/simple_form.pt-BR.yml +76 -0
  36. data/lib/base/project/lib/console_say.rb +17 -0
  37. data/lib/base/project/lib/string_sanitizer.rb +54 -0
  38. data/lib/base/project/version.rb +5 -0
  39. metadata +277 -0
@@ -0,0 +1,7 @@
1
+ require "base/project/version"
2
+
3
+ module Base
4
+ module Project
5
+ # Your code goes here...
6
+ end
7
+ end
@@ -0,0 +1,104 @@
1
+
2
+ module Base::Project::App::Controllers::Concerns::BaseCrudController
3
+ extend ActiveSupport::Concern
4
+
5
+ @serializer_class ||= nil
6
+
7
+ included do
8
+ before_action :check_default_permission
9
+ before_action :set_model, only: %i[show update destroy]
10
+
11
+ def index
12
+ @models = apply_scopes(model_source)
13
+
14
+ respond_with(@models) do |format|
15
+ format.html { flash.now[:info] = t('simple_form.results', count: @models.size) }
16
+ format.json { render json: @models, serializer_each: @serializer_class }
17
+ end
18
+ end
19
+
20
+ def show
21
+ respond_with(@model) do |format|
22
+ format.html {}
23
+ format.json { render json: @model, serializer: @serializer_class }
24
+ end
25
+ end
26
+
27
+ def new
28
+ @model = model_source.new
29
+
30
+ respond_with(@model) do |format|
31
+ format.html {}
32
+ format.json { render json: @model, serializer: @serializer_class }
33
+ end
34
+ end
35
+
36
+ def create
37
+ @model = model_source.new(model_params)
38
+ is_save = @model.save
39
+
40
+ respond_with(@model) do |format|
41
+ format.html do
42
+ if is_save
43
+ redirect_to created_path, notice: t('simple_form.added', klass: model_name)
44
+ else
45
+ flash.now[:error] = t('simple_form.error_notification.default_message')
46
+ render action: 'new'
47
+ end
48
+ end
49
+
50
+ format.json do
51
+ if is_save
52
+ head :created
53
+ else
54
+ render json: @model.errors, status: :unprocessable_entity
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ def update
61
+ is_save = @model.update(update_model_params)
62
+
63
+ respond_with(@model) do |format|
64
+ format.html do
65
+ if is_save
66
+ flash.now[:success] = t('simple_form.updated', klass: model_name)
67
+ else
68
+ flash.now[:error] = t('simple_form.error_notification.default_message')
69
+ render action: 'show'
70
+ end
71
+ end
72
+
73
+ format.json do
74
+ if is_save
75
+ head :no_content
76
+ else
77
+ render json: @model.errors, status: :unprocessable_entity
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ def destroy
84
+ @model.destroy
85
+
86
+ respond_to do |format|
87
+ format.html { redirect_to destroyed_path, notice: t('simple_form.removed', klass: model_name) }
88
+ format.json { head :no_content }
89
+ end
90
+ end
91
+
92
+ private
93
+
94
+ def set_model
95
+ @model = model_source.find(params[:id])
96
+ end
97
+ end
98
+
99
+ module ClassMethods
100
+ def serializer(klass)
101
+ @serializer_class = klass
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,128 @@
1
+
2
+ module Base::Project::App::Helpers::BaseHelper
3
+ def bootstrap_class_for(flash_type)
4
+ case flash_type.to_sym
5
+ when :success
6
+ 'alert-success' # Green
7
+ when :error
8
+ 'alert-danger' # Red
9
+ when :alert
10
+ 'alert-warning' # Yellow
11
+ when :notice
12
+ 'alert-success' # Blue
13
+ when :info
14
+ 'alert-info' # Blue
15
+ else
16
+ flash_type.to_s
17
+ end
18
+ end
19
+
20
+ def show_flash(type, message)
21
+ klass = bootstrap_class_for(type)
22
+
23
+ content_tag(:div, class: "alert alert-dismissible fade in #{klass} flash-message", role: 'alert') do
24
+ concat(button_tag(type: 'button', class: 'close', data: { dismiss: 'alert', label: 'Close' }) do
25
+ content_tag(:span, 'x', aria: { hidden: true })
26
+ end)
27
+
28
+ concat(message)
29
+ end
30
+ end
31
+
32
+ def show_flashes(flash)
33
+ flash.each do |type, message|
34
+ if message.is_a?(Array)
35
+ message.each do |text|
36
+ show_flash(type, text)
37
+ end
38
+
39
+ else
40
+ show_flash(type, message)
41
+ end
42
+ end
43
+ end
44
+
45
+ def page_title(text)
46
+ title = t('application_name')
47
+ text.blank? ? title : "#{title} - #{text}"
48
+ end
49
+
50
+ def nested_text_field(options = {})
51
+ text_field_tag(options[:field_name], options[:value], type: :text, class: "form-control #{options[:class]}")
52
+ end
53
+
54
+ def nested_select(options = {})
55
+ select_tag(options[:field_name], options[:value], class: "form-control tag-select #{options[:class]}")
56
+ end
57
+
58
+ def menu_item(path, icon, text, additional = {})
59
+ options = { method: :get, remote: false }.merge(additional)
60
+
61
+ content_tag(:li) do
62
+ link_to path, method: options[:method], remote: options[:remote] do
63
+ concat(content_tag(:i, '', class: "#{icon.split('-').first} #{icon}"))
64
+ concat(text)
65
+ end
66
+ end
67
+ end
68
+
69
+ def log_out_menu_item(path, icon, text)
70
+ menu_item(path, icon, text, method: :delete)
71
+ end
72
+
73
+ def icon_link(path, icon, text)
74
+ link_to(path) do
75
+ concat content_tag(:span, '', class: icon)
76
+ concat ' '
77
+ concat text
78
+ end
79
+ end
80
+
81
+ def brand_link
82
+ link_to(t('application_name'), authenticated_user_path, class: 'navbar-brand')
83
+ end
84
+
85
+ def link_image(image, options = {})
86
+ link_to(image.url(:big), target: '_blank', title: options[:title]) do
87
+ image_tag(image.url(:thumb), class: 'text-center img-circle', title: options[:title])
88
+ end
89
+ end
90
+
91
+ def edit_link(destination)
92
+ link_to(content_tag(:span, '', class: 'fa fa-pencil'), destination, class: 'pull-right')
93
+ end
94
+
95
+ def cancel_link(destination)
96
+ round_icon_link(path: destination, type: :button, icon: 'fa-close', button: 'btn-default', class: 'pull-right m-l-5', title: t('wizard.button.cancel'))
97
+ end
98
+
99
+ def submit_link
100
+ round_icon_button type: :submit, icon: 'fa-save', button: 'btn-success', class: 'pull-right', title: t('wizard.button.finish')
101
+ end
102
+
103
+ def round_icon_link(params)
104
+ params[:method] ||= :get
105
+ params[:remote] ||= false
106
+ data = {}
107
+
108
+ data[:confirm] = params[:confirm] if params[:confirm]
109
+
110
+ link_to(params[:path], method: params[:method], data: data, remote: params[:remote]) do
111
+ round_icon_button(params)
112
+ end
113
+ end
114
+
115
+ def text_icon_link(params)
116
+ params[:shape] = ''
117
+ params[:text] = params[:title]
118
+ params[:method] ||= :get
119
+ params[:remote] ||= false
120
+ data = {}
121
+
122
+ data[:confirm] = params[:confirm] if params[:confirm]
123
+
124
+ link_to(params[:path], method: params[:method], data: data, remote: params[:remote]) do
125
+ text_icon_button(params)
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,101 @@
1
+
2
+ module Base::Project::App::Helpers::ButtonHelper
3
+ def command_button( path, row_id, message, additional = { } )
4
+ options = { remote: false, role: :get, target: '_self', klass: '',
5
+ text: "", confirm: false, data: { } }.merge( additional )
6
+
7
+ text = options[:text]
8
+ role = options[:role]
9
+ icon = options[:icon]
10
+ klass = options[:klass]
11
+ is_remote = options[:remote]
12
+ html_data = options[:data]
13
+
14
+ data_params = html_data.merge( { toggle: 'tooltip', placement: 'top', 'row-id': row_id, 'original-title': message } )
15
+ data_params[:confirm] = I18n.t('confirmation') if options[:confirm]
16
+
17
+ button = command_button_item( klass, icon, text, data_params )
18
+
19
+ if path.present?
20
+ link_to(path, remote: is_remote, data: data_params, target: options[:target], method: role) do
21
+ concat( button )
22
+ end
23
+
24
+ else
25
+ button
26
+ end
27
+ end
28
+
29
+ def command_button_item( klass, icon, text, data )
30
+ text = " #{text}" if text.present?
31
+
32
+ button_tag( role: 'button', type: 'button', class: "btn btn-primary #{ klass }", data: data ) do
33
+ content_tag( :span, text, class: icon )
34
+ end
35
+ end
36
+
37
+ def edit_button( path, row_id, message, additional = { } )
38
+ options = { role: :get, icon: 'zmdi zmdi-edit' }.merge( additional )
39
+ command_button( path, row_id, I18n.t('simple_form.edit', message: message), options )
40
+ end
41
+
42
+ def new_button( path, message, additional = { } )
43
+ options = { role: :get, icon: 'zmdi zmdi-plus' }.merge( additional )
44
+ command_button( path, 0, I18n.t('simple_form.new', message: message), options )
45
+ end
46
+
47
+ def delete_button( path, row_id, message, additional = { } )
48
+ options = { role: :delete, icon: 'zmdi zmdi-delete', klass: 'command-delete', confirm: true }.merge( additional )
49
+ command_button( path, row_id, I18n.t('simple_form.delete', message: message), options )
50
+ end
51
+
52
+ def show_button( path, row_id, message, additional = { } )
53
+ options = { role: :get, icon: 'zmdi zmdi-search' }.merge( additional )
54
+ command_button( path, row_id, I18n.t('simple_form.show', message: message), options )
55
+ end
56
+
57
+ def open_filter_button
58
+ button_tag( t('simple_form.filter'), id: 'enable-filter',
59
+ class: 'btn btn-regular pull-left' )
60
+ end
61
+
62
+ def do_filter_button
63
+ button_tag( t('simple_form.search'), id: 'enable-filter',
64
+ class: 'btn btn-primary pull-right' )
65
+ end
66
+
67
+ def cancel_button
68
+ round_icon_button(type: :button, icon: 'fa-close', button: 'btn-default', class: 'pull-right', title: 'Cancelar')
69
+ end
70
+
71
+ def round_icon_button(params)
72
+ params[:shape] = 'btn-circle'
73
+ params[:text] = ''
74
+
75
+ text_icon_button(params)
76
+ end
77
+
78
+ def text_icon_button(params)
79
+ params[:size] ||= 'btn-lg'
80
+
81
+ content = content_tag(:span) do
82
+ concat(content_tag(:span, '', class: "fa #{params[:icon]}"))
83
+ concat(" #{params[:text]}") if params[:text] && params[:text].length > 0
84
+ end
85
+
86
+ button_data = { type: params[:type],
87
+ class: "btn #{params[:button]} #{params[:shape]} #{params[:size]} #{params[:class]} text-center",
88
+ title: params[:title],
89
+ data: {
90
+ toggle: 'tooltip',
91
+ placement: 'bottom'
92
+ }
93
+ }
94
+
95
+
96
+ button_data[:id] = params[:id] if params[:id]
97
+ button_data[:data] = button_data[:data].merge(params[:data]) if params[:data]
98
+
99
+ button_tag(content, button_data)
100
+ end
101
+ end
@@ -0,0 +1,51 @@
1
+
2
+ module Base::Project::App::Helpers::LocalizableHelper
3
+ def prepare_coordinates(location)
4
+ coordinate_array = location.coordinates
5
+ coordinate_array.present? ? escape_javascript(coordinate_array.to_json).to_s : ''
6
+ end
7
+
8
+ def add_localization(location)
9
+ add_localizations([location]) if location
10
+ end
11
+
12
+ def add_localizations(locations)
13
+ return '' if locations.blank?
14
+
15
+ js_locations = []
16
+ first_location = locations.first
17
+
18
+ been_img = image_url('map/been.png')
19
+ current_img = image_url('map/employee.png')
20
+
21
+ locations.each do |location|
22
+ coordinates = prepare_coordinates(location)
23
+ next if coordinates.blank?
24
+
25
+ if location != first_location
26
+ image = been_img
27
+ current = true
28
+ else
29
+ image = current_img
30
+ current = false
31
+ end
32
+
33
+ location_id = "loc-#{location.id}"
34
+
35
+ js_locations << "window.map.addCoordinate({
36
+ coordinates: #{coordinates},
37
+ icon: '#{image}',
38
+ locationId: '#{location_id}',
39
+ visited: #{current},
40
+ period: true,
41
+ properties: { title: '#{location.name}', icon: '#{image}' }
42
+ });"
43
+ end
44
+
45
+ js_locations << "window.map.getMap().setCenter(#{prepare_coordinates(first_location)});"
46
+ js_locations << 'window.map.getMap().setZoom(15);'
47
+
48
+ result = js_locations.join("\n")
49
+ result.html_safe
50
+ end
51
+ end
@@ -0,0 +1,8 @@
1
+
2
+ module Base::Project::App::Helpers::PanelHelper
3
+ def do_panel(params, &block)
4
+ params[:block] = block
5
+ params[:color] ||= 'panel-primary'
6
+ render 'shared/panels/panel', params
7
+ end
8
+ end
@@ -0,0 +1,93 @@
1
+ module Base::Project::App::Models::Concerns::Addressable
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ before_validation :coordinates_precision
6
+ normalize_attributes :zipcode, with: :remove_punctuation
7
+
8
+ validates :default, inclusion: { in: [true, false] }
9
+ validates :name, presence: true, length: { maximum: 100 }
10
+
11
+ validates :country, presence: true, length: { maximum: 2, minimum: 2 }
12
+ validates :state, presence: true, length: { maximum: 100 }
13
+ validates :city, presence: true, length: { maximum: 100 }
14
+ validates :street, presence: true, length: { maximum: 100 }
15
+ validates :number, presence: true, numericality: { greater_than_or_equal_to: 0 }
16
+ validates :district, presence: true, length: { maximum: 100 }
17
+ validates :zipcode, presence: true, length: { maximum: 8, minimmum: 8 }, format: { with: /\A\d*\z/i }
18
+
19
+ validates :apartment, presence: false, numericality: { greater_than: 0 }, allow_blank: true
20
+ validates :block, presence: false, length: { maximum: 100 }, allow_blank: true
21
+
22
+ validates :latitude, presence: false, allow_blank: true
23
+ validates :longitude, presence: false, allow_blank: true
24
+ validate :unique_default_address
25
+
26
+ scope :by_name, ->(name) { where("#{table_name}.name ILIKE ?", "%#{name}%") }
27
+ scope :by_country, ->(country) { where("#{table_name}.country ILIKE ?", "%#{country}%") }
28
+ scope :by_city, ->(city) { where("#{table_name}.city ILIKE ?", "%#{city}%") }
29
+ scope :by_street, ->(street) { where("#{table_name}.street ILIKE ?", "%#{street}%") }
30
+ scope :by_number, ->(number) { where("#{table_name}.number = ?", number.to_i.to_s) }
31
+ scope :by_district, ->(district) { where("#{table_name}.district ILIKE ?", "%#{district}%") }
32
+ scope :by_zipcode, ->(zipcode) { where("#{table_name}.zipcode ILIKE ?", "%#{Base::Project::Lib::StringSanitizer.remove_punctuation(zipcode)}%") }
33
+
34
+ scope :regular_order, -> { order("#{table_name}.default DESC, #{table_name}.name") }
35
+ scope :the_defaults, -> { where("#{table_name}.default = ?", true) }
36
+ end
37
+
38
+ def coordinates_precision
39
+ self.latitude = latitude.round(11) if latitude.present?
40
+ self.longitude = longitude.round(11) if longitude.present?
41
+ end
42
+
43
+ def multiple_default_error
44
+ errors.add(:default, I18n.t('activerecord.errors.messages.default_address_already_exists'))
45
+ end
46
+
47
+ def formatted_simple
48
+ text = "#{street} #{number}"
49
+
50
+ text = "#{text}, ap.#{apartment}" if apartment
51
+ text = "#{text}, bl.#{block}" if block
52
+
53
+ "#{text}, #{district}, #{city}"
54
+ end
55
+
56
+ def formatted_zipcode
57
+ StringSanitizer.mask_cep(zipcode)
58
+ end
59
+
60
+ def formatted_country
61
+ country_model = ISO3166::Country[country]
62
+ country_model.translations[country] || country_model.name
63
+ end
64
+
65
+ def formatted_complete
66
+ "#{formatted_simple}, #{formatted_zipcode} #{formatted_country}"
67
+ end
68
+
69
+ def coordinate_pair
70
+ [latitude, longitude]
71
+ end
72
+
73
+ def coordinates_dms
74
+ latitude_dms = coordinate_to_dms(latitude, %w[N S])
75
+ longitude_dms = coordinate_to_dms(longitude, %w[E W])
76
+
77
+ [latitude_dms, longitude_dms]
78
+ end
79
+
80
+ private
81
+
82
+ def coordinate_to_dms(coordinate_value, directions)
83
+ value = coordinate_value.abs
84
+
85
+ degrees = value.floor
86
+ direction = (latitude > 0 ? directions.first : directions.last)
87
+
88
+ minutes = (((value - degrees) * 60)).round(3).floor
89
+ seconds = (((value - degrees) * 3600) - (minutes * 60)).round(3).floor
90
+
91
+ "#{degrees}° #{minutes}′ #{seconds}\"#{direction}"
92
+ end
93
+ end