frontpack 0.1.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.
Files changed (70) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +51 -0
  4. data/Rakefile +5 -0
  5. data/app/assets/config/frontpack_manifest.js +1 -0
  6. data/app/assets/images/logo.svg +141 -0
  7. data/app/assets/javascripts/frontpack/auto-complete.js +173 -0
  8. data/app/assets/javascripts/frontpack/base-webcomponent.js +160 -0
  9. data/app/assets/javascripts/frontpack/expandable-list.js +99 -0
  10. data/app/assets/javascripts/frontpack/image-preview.js +160 -0
  11. data/app/assets/javascripts/frontpack/image-uploader.js +34 -0
  12. data/app/assets/javascripts/frontpack/localized-time.js +17 -0
  13. data/app/assets/javascripts/frontpack/modal-dialog.js +206 -0
  14. data/app/assets/javascripts/frontpack/shortcuts.js +63 -0
  15. data/app/assets/javascripts/frontpack/tab-control.js +77 -0
  16. data/app/assets/javascripts/frontpack/text-box.js +70 -0
  17. data/app/assets/javascripts/frontpack/toggle-button.js +73 -0
  18. data/app/assets/stylesheets/frontpack/000-reset.scss +15 -0
  19. data/app/assets/stylesheets/frontpack/010-config.scss +148 -0
  20. data/app/assets/stylesheets/frontpack/020-mixins.scss +218 -0
  21. data/app/assets/stylesheets/frontpack/030-consts.scss +13 -0
  22. data/app/assets/stylesheets/frontpack/031-margins.scss +5 -0
  23. data/app/assets/stylesheets/frontpack/100-layout.scss +113 -0
  24. data/app/assets/stylesheets/frontpack/101-container.scss +131 -0
  25. data/app/assets/stylesheets/frontpack/200-design.scss +75 -0
  26. data/app/assets/stylesheets/frontpack/201-box.scss +6 -0
  27. data/app/assets/stylesheets/frontpack/202-borders.scss +5 -0
  28. data/app/assets/stylesheets/frontpack/300-typography.scss +128 -0
  29. data/app/assets/stylesheets/frontpack/800-components.scss +85 -0
  30. data/app/assets/stylesheets/frontpack/801-card.scss +53 -0
  31. data/app/assets/stylesheets/frontpack/802-buttons.scss +58 -0
  32. data/app/assets/stylesheets/frontpack/803-form-controls.scss +161 -0
  33. data/app/assets/stylesheets/frontpack/804-table.scss +60 -0
  34. data/app/assets/stylesheets/frontpack/805-switch-toggle.scss +43 -0
  35. data/app/assets/stylesheets/frontpack/frontpack.scss +18 -0
  36. data/app/controllers/concerns/frontpack/searchable.rb +33 -0
  37. data/app/controllers/frontpack/autocomplete_controller.rb +33 -0
  38. data/app/helpers/frontpack/application_helper.rb +7 -0
  39. data/app/helpers/frontpack/form_builder.rb +130 -0
  40. data/app/models/concerns/frontpack/auto_completable.rb +35 -0
  41. data/config/importmap.rb +1 -0
  42. data/config/routes.rb +3 -0
  43. data/lib/frontpack/button_to_patch.rb +64 -0
  44. data/lib/frontpack/engine.rb +28 -0
  45. data/lib/frontpack/extended_model_translations.rb +31 -0
  46. data/lib/frontpack/form_builder_options.rb +5 -0
  47. data/lib/frontpack/version.rb +5 -0
  48. data/lib/frontpack.rb +9 -0
  49. data/lib/generators/frontpack/install_generator.rb +37 -0
  50. data/lib/generators/frontpack/locale_generator.rb +11 -0
  51. data/lib/generators/templates/initializers/customize_form_with_errors.rb +31 -0
  52. data/lib/generators/templates/locales/frontpack.en.yml +43 -0
  53. data/lib/generators/templates/views/layouts/_form-errors.html.slim +3 -0
  54. data/lib/generators/templates/views/layouts/_navigation.html.slim +1 -0
  55. data/lib/generators/templates/views/layouts/application.html.slim +37 -0
  56. data/lib/generators/templates/views/layouts/errors.html.slim +19 -0
  57. data/lib/generators/templates/views/layouts/mailer.html.slim +6 -0
  58. data/lib/generators/templates/views/layouts/mailer.text.slim +1 -0
  59. data/lib/tasks/frontpack_tasks.rake +6 -0
  60. data/lib/templates/rails/file_utils/searchable.rb +30 -0
  61. data/lib/templates/rails/scaffold_controller/controller.rb.tt +93 -0
  62. data/lib/templates/slim/scaffold/_form.html.slim.tt +10 -0
  63. data/lib/templates/slim/scaffold/edit.html.slim.tt +10 -0
  64. data/lib/templates/slim/scaffold/index.html.slim.tt +20 -0
  65. data/lib/templates/slim/scaffold/new.html.slim.tt +8 -0
  66. data/lib/templates/slim/scaffold/partial.html.slim.tt +6 -0
  67. data/lib/templates/slim/scaffold/show.html.slim.tt +12 -0
  68. data/lib/templates/test_unit/model/fixtures.yml.tt +18 -0
  69. data/lib/templates/test_unit/model/unit_test.rb.tt +41 -0
  70. metadata +143 -0
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Frontpack
4
+ # Adds dynamic search functionality
5
+ module Searchable
6
+ extend ActiveSupport::Concern
7
+
8
+ def basic_search(model, approximate_fields = [], exact_fields = [], include_list = nil)
9
+ search_conditions = prepare_search_fields model.arel_table, exact_fields, exact: true
10
+ search_conditions += prepare_search_fields model.arel_table, approximate_fields
11
+
12
+ query = include_list.present? ? model.includes(include_list) : model
13
+ query = query.where(*search_conditions) if search_conditions.length.positive?
14
+
15
+ query.page(params[:page]).per(params[:per_page])
16
+ end
17
+
18
+ def prepare_search_fields(arel_table, fields, **options)
19
+ [].tap do |search_conditions|
20
+ fields.each do |field|
21
+ val = params[field]
22
+ next if val.blank?
23
+
24
+ if options[:exact]
25
+ search_conditions.push(arel_table[field].eq(val))
26
+ else
27
+ search_conditions.push(arel_table[field].matches(Arel::Nodes::SqlLiteral.new("'%#{val}%'")))
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,33 @@
1
+ module Frontpack
2
+ # Autocomplete controller
3
+ class AutocompleteController < ApplicationController
4
+ before_action :set_model
5
+
6
+ def show
7
+ query = @model.autocomplete(params[:keyword], **autocomplete_params)
8
+ data = query.map do |row|
9
+ if row.is_a? String
10
+ { id: row, value: row }
11
+ else
12
+ {
13
+ id: row.shift,
14
+ value: row.join(' ')
15
+ }
16
+ end
17
+ end
18
+ respond_to do |format|
19
+ format.json { render json: data }
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def set_model
26
+ @model = params[:model].split('/').map(&:classify).join('::').constantize
27
+ end
28
+
29
+ def autocomplete_params
30
+ params.permit(*@model.autocomplete_fields)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,7 @@
1
+ module Frontpack
2
+ module ApplicationHelper
3
+ def boolean_emoji(boolean)
4
+ boolean ? '✅' : '❌'
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Frontpack
4
+ # Custom input builders
5
+ class FormBuilder < ActionView::Helpers::FormBuilder
6
+ include Rails.application.routes.url_helpers
7
+ include ActionView::Helpers::UrlHelper
8
+
9
+ def enum_field(method, options = {})
10
+ excluded_keys = options.key?(:except) ? options[:except] : []
11
+
12
+ if @object
13
+ opts = { selected: @object.send(method) }
14
+ select method, @object.enum_options(method, excluded_keys), opts, options
15
+ elsif options.key?(:model)
16
+ model = options[:model]
17
+ select method, model.enum_options(method, excluded_keys), options.slice(:selected, :include_blank, :prompt), options
18
+ else
19
+ select method, [], opts, options
20
+ end
21
+ end
22
+
23
+ def autocomplete_field(method, options = {})
24
+ relation = @object.class.reflect_on_association(method)
25
+ uri = relation.class_name.underscore.sub('::', '/')
26
+ display_field = relation.klass.autocomplete_display_fields[1]
27
+ text_field(relation.foreign_key, options.merge('data-url': "/autocomplete/#{uri}", is: 'auto-complete', 'display-value': @object.send(method)&.send(display_field)))
28
+ end
29
+
30
+ def text_field(method, options = {})
31
+ set_options(method, options) if @object
32
+ super(method, options)
33
+ end
34
+
35
+ def password_field(method, options = {})
36
+ set_options(method, options) if @object
37
+ super
38
+ end
39
+
40
+ def email_field(method, options = {})
41
+ set_options(method, options) if @object
42
+ super
43
+ end
44
+
45
+ def text_area(method, options = {})
46
+ set_options(method, options) if @object
47
+ super
48
+ end
49
+
50
+ def label(method, text = nil, options = {}, &)
51
+ set_label_options method, options if @object
52
+ super(method, text, options, &)
53
+ end
54
+
55
+ def select(method, choices = nil, options = {}, html_options = {}, &)
56
+ set_options(method, html_options, css_class: Frontpack::FormBuilderOptions.select_class) if @object
57
+ super
58
+ end
59
+
60
+ def number_field(method, options = {})
61
+ set_options(method, options) if @object
62
+
63
+ super(method, options)
64
+ end
65
+
66
+ def date_field(method, options = {}, _html_options = {})
67
+ @template.text_field(@object_name, method, objectify_options(options.merge(type: :date)))
68
+ end
69
+
70
+ def switch_field(method, options = {})
71
+ @template.content_tag(:div, class: 'switch-toggle') do
72
+ toggle_box = check_box(method, { checked: @object.send(method) }.merge(options))
73
+ opts = options.merge(for: "#{@object_name}_#{method}")
74
+ opts[:style] = "--on-icon: '#{options[:'data-on-icon']}';" if options.key? :'data-on-icon'
75
+ opts[:style] += "--off-icon: '#{options[:'data-off-icon']}';" if options.key? :'data-off-icon'
76
+ toggle_box + @template.content_tag(:label, nil, opts)
77
+ end
78
+ end
79
+
80
+ private
81
+
82
+ def set_options(method, options, css_class: Frontpack::FormBuilderOptions.input_class)
83
+ @object.class.validators.map do |validator|
84
+ next unless validator.attributes.include?(method)
85
+
86
+ option_required(options) if validator.is_a? ::ActiveRecord::Validations::PresenceValidator
87
+ options[:maxlength] = validator.options[:maximum] if validator.is_a? ActiveRecord::Validations::LengthValidator
88
+ options[:size] = nil
89
+ set_numericality_options(validator, options) if validator.is_a? ActiveModel::Validations::NumericalityValidator
90
+ end
91
+ merge_css_classes(options, css_class)
92
+ end
93
+
94
+ def merge_css_classes(options, css_class)
95
+ classes = []
96
+ classes += options[:class].to_s.split if options.key? :class
97
+ classes << css_class
98
+ options[:class] = classes.join ' '
99
+ end
100
+
101
+ # @param [Hash] options
102
+ def option_required(options)
103
+ if options.key?(:required) && options[:required] == false
104
+ options.delete(:required)
105
+ return options
106
+ end
107
+
108
+ options[:required] = true
109
+ end
110
+
111
+ def set_numericality_options(validator, options)
112
+ if (gt = validator.options[:greater_than])
113
+ options[:min] = gt
114
+ end
115
+ if (lt = validator.options[:less_than])
116
+ options[:max] = lt
117
+ end
118
+ options[:step] = :any
119
+ end
120
+
121
+ def set_label_options(method, options)
122
+ @object.class.validators.map do |validator|
123
+ next unless validator.attributes.include?(method)
124
+
125
+ options[:'data-required'] = true if validator.is_a?(ActiveRecord::Validations::PresenceValidator)
126
+ end
127
+ merge_css_classes options, Frontpack::FormBuilderOptions.label_class
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,35 @@
1
+ module Frontpack
2
+ module AutoCompletable
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ cattr_reader :autocomplete_keyword, :autocomplete_fields
7
+ @autocomplete_display_fields = []
8
+ @autocomplete_key = :id
9
+ end
10
+
11
+ class_methods do
12
+ def autocomplete_by(keyword, *extra_filters)
13
+ @autocomplete_keyword = keyword.to_sym
14
+ @autocomplete_fields = extra_filters.map(&:to_sym)
15
+ end
16
+
17
+ def autocomplete_with(key, *display_fields)
18
+ @autocomplete_key = key.to_sym
19
+ @autocomplete_display_fields = display_fields.map(&:to_sym)
20
+ end
21
+
22
+ def autocomplete(keyword, **params)
23
+ keyword_field = arel_table[@autocomplete_keyword]
24
+ condition = Arel::Nodes::SqlLiteral.new("'%#{keyword}%'")
25
+ query = where(keyword_field.matches(condition))
26
+ params.each { |key, value| query = query.where(arel_table[key].eq(value)) }
27
+ query.select(autocomplete_display_fields).pluck(autocomplete_display_fields)
28
+ end
29
+
30
+ def autocomplete_display_fields
31
+ [@autocomplete_key, *@autocomplete_display_fields]
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1 @@
1
+ pin_all_from File.expand_path('../app/assets/javascripts/frontpack', __dir__), under: 'frontpack'
data/config/routes.rb ADDED
@@ -0,0 +1,3 @@
1
+ Frontpack::Engine.routes.draw do
2
+ get 'autocomplete/*model/:keyword', to: 'autocomplete#show', as: :autocomplete
3
+ end
@@ -0,0 +1,64 @@
1
+ module ActionView
2
+ module Helpers
3
+ module UrlHelper
4
+ def button_to(name = nil, options = nil, html_options = nil, &block)
5
+ html_options, options = options, name if block_given?
6
+ html_options ||= {}
7
+ html_options = html_options.stringify_keys
8
+
9
+ url = case options
10
+ when FalseClass then nil
11
+ else url_for(options)
12
+ end
13
+
14
+ remote = html_options.delete("remote")
15
+ params = html_options.delete("params")
16
+
17
+ authenticity_token = html_options.delete("authenticity_token")
18
+
19
+ method = (html_options.delete("method").presence || method_for_options(options)).to_s
20
+ method_tag = BUTTON_TAG_METHOD_VERBS.include?(method) ? method_tag(method) : "".html_safe
21
+
22
+ form_id = html_options.delete("form_id") || SecureRandom.hex(8)
23
+ form_method = method == "get" ? "get" : "post"
24
+ form_options = html_options.delete("form") || {}
25
+ form_options[:class] ||= html_options.delete("form_class") || "button_to"
26
+ form_options[:method] = form_method
27
+ form_options[:action] = url
28
+ form_options[:id] = form_id
29
+ form_options[:'data-remote'] = true if remote
30
+
31
+ request_token_tag = if form_method == "post"
32
+ request_method = method.empty? ? "post" : method
33
+ token_tag(authenticity_token, form_options: { action: url, method: request_method })
34
+ else
35
+ ""
36
+ end
37
+
38
+ html_options = convert_options_to_data_attributes(options, html_options)
39
+ html_options["type"] = "submit"
40
+
41
+
42
+ html_options["form"] = form_id
43
+ button = if block_given?
44
+ content_tag("button", html_options, &block)
45
+ elsif button_to_generates_button_tag
46
+ content_tag("button", name || url, html_options, &block)
47
+ else
48
+ html_options["value"] = name || url
49
+ tag("input", html_options)
50
+ end
51
+
52
+ inner_tags = method_tag.safe_concat(request_token_tag)
53
+ if params
54
+ to_form_params(params).each do |param|
55
+ inner_tags.safe_concat tag(:input, type: "hidden", name: param[:name], value: param[:value],
56
+ autocomplete: "off")
57
+ end
58
+ end
59
+ html = content_tag("form", inner_tags, form_options) + button
60
+ prevent_content_exfiltration(html)
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'importmap-rails'
4
+
5
+ module Frontpack
6
+ class Engine < ::Rails::Engine
7
+ isolate_namespace Frontpack
8
+
9
+ initializer 'frontpack.importmap', before: 'importmap' do |app|
10
+ app.config.importmap.paths << root.join('config/importmap.rb')
11
+ app.config.importmap.cache_sweepers << root.join('app/assets/javascripts')
12
+ end
13
+
14
+ initializer 'frontpack.assets' do |app|
15
+ app.config.assets.precompile += %w[frontpack_manifest]
16
+ end
17
+
18
+ initializer 'frontpack.action_controller' do |app|
19
+ ActiveSupport.on_load :action_controller do
20
+ helper Frontpack::ApplicationHelper
21
+ end
22
+ end
23
+
24
+ initializer 'frontpack.active_record' do
25
+ ActiveRecord::Base.include Frontpack::ExtendedModelTranslations
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,31 @@
1
+ module Frontpack
2
+ module ExtendedModelTranslations
3
+ extend ActiveSupport::Concern
4
+ include ActiveModel::Naming
5
+
6
+ class_methods do
7
+ def human_enum_value(enum_name, enum_value)
8
+ I18n.t("#{i18n_scope}.attributes.#{model_name.i18n_key}.#{enum_name.to_s.pluralize}.#{enum_value}")
9
+ end
10
+
11
+ def enum_options(enum_name, excluded_keys = [])
12
+ keys = send(enum_name.to_s.pluralize).keys.reject { |item| excluded_keys.include? item.to_sym }
13
+ keys.collect { |item| [human_enum_value(enum_name, item), item] }
14
+ end
15
+ end
16
+
17
+ delegate :human_attribute_name, to: :class
18
+
19
+ def enum_options(enum_name, excluded_keys = [])
20
+ self.class.enum_options(enum_name, excluded_keys)
21
+ end
22
+
23
+ def human_enum_name(enum_name)
24
+ self.human_enum_name(enum_name)
25
+ end
26
+
27
+ def human_enum_value(enum_name)
28
+ self.class.human_enum_value(enum_name, self[enum_name])
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,5 @@
1
+ module Frontpack
2
+ module FormBuilderOptions
3
+ mattr_accessor :input_class, :label_class, :select_class
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Frontpack
4
+ VERSION = '0.1.0'
5
+ end
data/lib/frontpack.rb ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'frontpack/extended_model_translations'
4
+ require 'frontpack/version'
5
+ require 'frontpack/engine'
6
+ require 'frontpack/form_builder_options'
7
+ require 'frontpack/button_to_patch'
8
+
9
+ module Frontpack; end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Frontpack
4
+ class InstallGenerator < Rails::Generators::Base
5
+ source_root File.expand_path('../templates', __dir__)
6
+
7
+ def patch_app_base_classes
8
+ inject_into_class 'app/controllers/application_controller.rb', ApplicationController, <<-RUBY
9
+ # Frontpack form builder
10
+ default_form_builder Frontpack::FormBuilder
11
+ RUBY
12
+ copy_file 'initializers/customize_form_with_errors.rb', 'config/initializers/customize_form_with_errors.rb'
13
+ end
14
+
15
+ def add_frontpack_assets
16
+ prepend_file 'app/assets/stylesheets/application.scss', <<~SCSS
17
+ @use 'frontpack/frontpack' with (
18
+ $theme-mode: 'dark',
19
+ $background: #2c0408,
20
+ $highlight: #dcac0b,
21
+ );
22
+
23
+ SCSS
24
+ end
25
+
26
+ def copy_layout
27
+ copy_file 'views/layouts/_form-errors.html.slim', 'app/views/layouts/_form-errors.html.slim'
28
+ copy_file 'views/layouts/_navigation.html.slim', 'app/views/layouts/_navigation.html.slim'
29
+ copy_file 'views/layouts/errors.html.slim', 'app/views/layouts/errors.html.slim'
30
+ template 'views/layouts/application.html.slim', 'app/views/layouts/application.html.slim'
31
+ end
32
+
33
+ def copy_locale
34
+ copy_file 'locales/frontpack.en.yml', 'config/locales/frontpack.en.yml'
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Frontpack
4
+ class LocaleGenerator < Rails::Generators::Base
5
+ source_root File.expand_path('../templates', __dir__)
6
+
7
+ def install
8
+ copy_file 'locale/frontpack.en_US.yml', 'config/locale/frontpack.en_US.yml'
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Disable the form_with_errors feature
4
+ module ActionView
5
+ module Helpers
6
+ module Tags
7
+ # Allows access to method name
8
+ class Base
9
+ attr_reader :method_name
10
+ end
11
+ end
12
+ end
13
+ end
14
+
15
+ ActionView::Base.field_error_proc = proc do |html_tag, instance|
16
+ if instance.is_a? ActionView::Helpers::Tags::Label
17
+ html_tag
18
+ else
19
+ class_attr_index = html_tag.index 'class="'
20
+
21
+ if class_attr_index
22
+ html_tag.insert class_attr_index + 7, 'error '
23
+ else
24
+ html_tag.insert html_tag.index('>'), ' class=error'
25
+ end
26
+
27
+ # rubocop:disable Rails/OutputSafety
28
+ html_tag.safe_concat "<i>#{instance.object.errors[instance.method_name][0].capitalize}</i>"
29
+ # rubocop:enable Rails/OutputSafety
30
+ end
31
+ end
@@ -0,0 +1,43 @@
1
+ ---
2
+ en:
3
+ sign_in: Sign in
4
+ sign_out: Sign out
5
+ sign_up: Create account
6
+ recover_password: Recover password
7
+ resend_confirmation: Resend confirmation instructions
8
+ resend_unlock: Resend unlock instructions
9
+ home: Home
10
+ new: New
11
+ edit: Edit
12
+ update: Update
13
+ save: Save
14
+ delete: Delete
15
+ cancel: Cancel
16
+ back: Back
17
+ copy: Copy
18
+ created_successfully: "%{resource_name} created"
19
+ updated_successfully: "%{resource_name} updated"
20
+ deleted_successfully: "%{resource_name} destroyed"
21
+ admin_privileges_required: This resource requires admin rights
22
+ are_you_sure?: Are you sure?
23
+ ago: ago
24
+
25
+ activerecord:
26
+ attributes:
27
+ default_fields: &default_fields
28
+ created_at: Created at
29
+ updated_at: Updated at
30
+ views:
31
+ pagination:
32
+ last: '>>'
33
+ next: '>'
34
+ previous: '<'
35
+ first: '<<'
36
+ truncate: ...
37
+ layouts:
38
+ application:
39
+ loading: Loading...
40
+
41
+ status:
42
+ 404: The resource you are looking for does not exist
43
+ 500: The server encountered an internal error and was unable to complete your request
@@ -0,0 +1,3 @@
1
+ - if instance.errors.any?
2
+ - instance.errors.each do |error|
3
+ text-box.col-12.danger( icon = 'warning' )= "#{error.attribute} #{error.message}"
@@ -0,0 +1 @@
1
+ = link_to t(:home), root_url
@@ -0,0 +1,37 @@
1
+ doctype html
2
+ html
3
+ head
4
+ / TODO: Replace "Frontpack" with the desired default application title
5
+ title = content_for?(:title) ? yield(:title) : 'Frontpack'
6
+ meta(content = 'text/html;charset=UTF-8' http-equiv = 'Content-Type')
7
+ meta(content = 'width=device-width,initial-scale=1' name = 'viewport')
8
+ = csrf_meta_tags
9
+ = csp_meta_tag
10
+ = stylesheet_link_tag 'application', 'data-turbo-track': 'reload'
11
+ = javascript_importmap_tags
12
+ body
13
+ header#header
14
+ toggle-button#btn-menu.md-hidden.lg-hidden(type='toggle' data-icon="menu" target="main-menu" visible-class="-" hidden-class="sm-hidden")
15
+ = link_to root_url, id: :logo do
16
+ / TODO: Replace this image_tag with your logo
17
+ = image_tag 'logo.svg'
18
+ nav#main-menu.sm-hidden
19
+ = render 'layouts/navigation'
20
+ #profile
21
+ - if content_for? :breadcrumbs
22
+ nav#breadcrumbs
23
+ = link_to t(:home), root_url, class: 'no-underscore'
24
+ | /
25
+ = yield :breadcrumbs
26
+ #messages
27
+ - flash.each do |type, message|
28
+ text-box(class=type) = message
29
+ section#content
30
+ - if content_for? :actions
31
+ #actions = yield :actions
32
+ = yield
33
+ footer#footer
34
+ / TODO: Replace "Frontpack" with the desired copyright text
35
+ | &copy; #{Time.zone.today.year} - Frontpack
36
+
37
+ #loading= t('.loading')
@@ -0,0 +1,19 @@
1
+ html
2
+ head
3
+ meta(content = 'text/html;charset=UTF-8' http-equiv = 'Content-Type')
4
+ title Genesis
5
+ meta( content = 'width=device-width,initial-scale=1' name = 'viewport' )
6
+ = csrf_meta_tags
7
+ = csp_meta_tag
8
+ = stylesheet_link_tag 'application', 'data-turbo-track': 'reload'
9
+ = javascript_include_tag 'application', 'data-turbo-track': 'reload', defer: true
10
+ body
11
+ header#header
12
+ = link_to root_url do
13
+ = image_tag 'logo.svg', id: :logo
14
+ nav#main-menu
15
+ #profile
16
+ #messages
17
+ section#content= yield
18
+ footer#footer
19
+ | &copy; #{Time.zone.today.year} - Genesis
@@ -0,0 +1,6 @@
1
+ html
2
+ head
3
+ title Genesis
4
+ meta( content = 'text/html; charset=utf-8' http-equiv = 'Content-Type' )
5
+ body
6
+ = yield
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ # desc "Explaining what the task does"
4
+ # task :frontpack do
5
+ # # Task goes here
6
+ # end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Adds dynamic search functionality
4
+ module Searchable
5
+ extend ActiveSupport::Concern
6
+
7
+ def basic_search(model, approximate_fields = [], exact_fields = [], include_list = nil)
8
+ search_conditions = prepare_search_fields model.arel_table, exact_fields, exact: true
9
+ search_conditions += prepare_search_fields model.arel_table, approximate_fields
10
+
11
+ query = include_list.present? ? model.includes(include_list) : model
12
+ query = query.where(*search_conditions) if search_conditions.length.positive?
13
+
14
+ query.page(params[:page])
15
+ end
16
+
17
+ def prepare_search_fields(arel_table, fields, **options)
18
+ [].tap do |search_conditions|
19
+ fields.each do |field|
20
+ next if (val = params[field]).blank?
21
+
22
+ if options[:exact]
23
+ search_conditions.push(arel_table[field].eq(val))
24
+ else
25
+ search_conditions.push(arel_table[field].matches(Arel::Nodes::SqlLiteral.new("'%#{val}%'")))
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end