mensa 0.1.0 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/gem-push.yml +11 -0
  3. data/.gitignore +12 -0
  4. data/Gemfile +10 -8
  5. data/Gemfile.lock +299 -0
  6. data/MIT-LICENSE +20 -0
  7. data/Procfile +2 -0
  8. data/README.md +76 -18
  9. data/Rakefile +5 -13
  10. data/app/assets/config/mensa_manifest.js +5 -0
  11. data/app/assets/images/mensa/.keep +0 -0
  12. data/app/assets/stylesheets/mensa/application.css +11 -0
  13. data/app/components/mensa/add_filter/component.css +13 -0
  14. data/app/components/mensa/add_filter/component.html.slim +16 -0
  15. data/app/components/mensa/add_filter/component.rb +13 -0
  16. data/app/components/mensa/add_filter/component_controller.js +84 -0
  17. data/app/components/mensa/application_component.rb +12 -0
  18. data/app/components/mensa/cell/component.css +0 -0
  19. data/app/components/mensa/cell/component.html.slim +1 -0
  20. data/app/components/mensa/cell/component.rb +15 -0
  21. data/app/components/mensa/control_bar/component.css +5 -0
  22. data/app/components/mensa/control_bar/component.html.slim +15 -0
  23. data/app/components/mensa/control_bar/component.rb +21 -0
  24. data/app/components/mensa/filters/component.css +14 -0
  25. data/app/components/mensa/filters/component.html.slim +14 -0
  26. data/app/components/mensa/filters/component.rb +13 -0
  27. data/app/components/mensa/filters/component_controller.js +14 -0
  28. data/app/components/mensa/header/component.css +54 -0
  29. data/app/components/mensa/header/component.html.slim +8 -0
  30. data/app/components/mensa/header/component.rb +19 -0
  31. data/app/components/mensa/row_action/component.css +0 -0
  32. data/app/components/mensa/row_action/component.html.slim +6 -0
  33. data/app/components/mensa/row_action/component.rb +19 -0
  34. data/app/components/mensa/search/component.css +13 -0
  35. data/app/components/mensa/search/component.html.slim +17 -0
  36. data/app/components/mensa/search/component.rb +13 -0
  37. data/app/components/mensa/search/component_controller.js +62 -0
  38. data/app/components/mensa/table/component.css +0 -0
  39. data/app/components/mensa/table/component.html.slim +7 -0
  40. data/app/components/mensa/table/component.rb +16 -0
  41. data/app/components/mensa/table/component_controller.js +72 -0
  42. data/app/components/mensa/table_row/component.css +0 -0
  43. data/app/components/mensa/table_row/component.html.slim +6 -0
  44. data/app/components/mensa/table_row/component.rb +19 -0
  45. data/app/components/mensa/view/component.css +82 -0
  46. data/app/components/mensa/view/component.html.slim +17 -0
  47. data/app/components/mensa/view/component.rb +16 -0
  48. data/app/components/mensa/views/component.css +13 -0
  49. data/app/components/mensa/views/component.html.slim +18 -0
  50. data/app/components/mensa/views/component.rb +14 -0
  51. data/app/controllers/concerns/.keep +0 -0
  52. data/app/controllers/mensa/application_controller.rb +4 -0
  53. data/app/controllers/mensa/tables/filters_controller.rb +29 -0
  54. data/app/controllers/mensa/tables_controller.rb +37 -0
  55. data/app/helpers/mensa/application_helper.rb +5 -0
  56. data/app/helpers/mensa/tables_helper.rb +5 -0
  57. data/app/javascript/mensa/application.js +3 -0
  58. data/app/javascript/mensa/controllers/alert_controller.js +7 -0
  59. data/app/javascript/mensa/controllers/application.js +8 -0
  60. data/app/javascript/mensa/controllers/application_controller.js +57 -0
  61. data/app/javascript/mensa/controllers/index.js +24 -0
  62. data/app/jobs/mensa/application_job.rb +7 -0
  63. data/app/jobs/mensa/export_job.rb +91 -0
  64. data/app/models/concerns/.keep +0 -0
  65. data/app/models/mensa/application_record.rb +5 -0
  66. data/app/models/mensa/table_view.rb +5 -0
  67. data/app/tables/mensa/action.rb +26 -0
  68. data/app/tables/mensa/base.rb +103 -0
  69. data/app/tables/mensa/cell.rb +50 -0
  70. data/app/tables/mensa/column.rb +85 -0
  71. data/app/tables/mensa/config/action_dsl.rb +12 -0
  72. data/app/tables/mensa/config/column_dsl.rb +23 -0
  73. data/app/tables/mensa/config/dsl_logic.rb +90 -0
  74. data/app/tables/mensa/config/filter_dsl.rb +8 -0
  75. data/app/tables/mensa/config/render_dsl.rb +8 -0
  76. data/app/tables/mensa/config/table_dsl.rb +71 -0
  77. data/app/tables/mensa/config_readers.rb +13 -0
  78. data/app/tables/mensa/filter.rb +31 -0
  79. data/app/tables/mensa/row.rb +36 -0
  80. data/app/tables/mensa/scope.rb +99 -0
  81. data/app/views/layouts/mensa/application.html.slim +11 -0
  82. data/app/views/mensa/tables/filters/show.turbo_stream.slim +8 -0
  83. data/app/views/mensa/tables/index.html.slim +93 -0
  84. data/app/views/mensa/tables/show.html.slim +5 -0
  85. data/app/views/mensa/tables/show.turbo_stream.slim +5 -0
  86. data/bin/importmap +4 -0
  87. data/bin/rails +14 -0
  88. data/config/importmap.rb +8 -0
  89. data/config/locales/en.yml +8 -0
  90. data/config/locales/nl.yml +11 -0
  91. data/config/routes.rb +7 -0
  92. data/db/migrate/20240201184752_create_mensa_table_views.rb +15 -0
  93. data/lib/generators/mensa/install_generator.rb +24 -0
  94. data/lib/generators/mensa/tailwind_config_generator.rb +24 -0
  95. data/lib/generators/mensa/templates/config/initializers/mensa.rb +22 -0
  96. data/lib/mensa/configuration.rb +90 -0
  97. data/lib/mensa/engine.rb +37 -0
  98. data/lib/mensa/version.rb +1 -3
  99. data/lib/mensa.rb +24 -5
  100. data/lib/tasks/mensa_tasks.rake +13 -0
  101. data/mensa.gemspec +42 -0
  102. data/package.json +10 -0
  103. data/vendor/javascript/@rails--request.js.js +2 -0
  104. data/yarn.lock +837 -0
  105. metadata +245 -18
  106. data/.rubocop.yml +0 -13
  107. data/CHANGELOG.md +0 -5
  108. data/CODE_OF_CONDUCT.md +0 -84
  109. data/LICENSE.txt +0 -21
  110. data/sig/mensa.rbs +0 -4
@@ -0,0 +1,90 @@
1
+ module Mensa::Config
2
+ module DslLogic
3
+ extend ActiveSupport::Concern
4
+
5
+ attr_reader :config, :values
6
+
7
+ delegate :default_config, to: :class
8
+
9
+ def initialize(name, &)
10
+ @config = {}
11
+ @config[:name] = name if name
12
+ @config = @config.merge(default_config.deep_dup)
13
+
14
+ instance_eval(&) if block_given?
15
+ end
16
+
17
+ class_methods do
18
+ attr_reader :default_config
19
+
20
+ def option(name, default: nil, config_name: nil, dsl: nil, dsl_hash: nil, dsl_array: nil)
21
+ if dsl
22
+ dsl_option(name, dsl)
23
+ elsif dsl_hash
24
+ dsl_hash(name, dsl_hash, config_name: config_name)
25
+ elsif dsl_array
26
+ dsl_array(name, dsl_array, config_name: config_name)
27
+ else
28
+ simple_option(name, default: default)
29
+ end
30
+ end
31
+
32
+ ###
33
+ # Define a accessor method
34
+ #
35
+ # def name(value = nil, &)
36
+ # config[:name] = block || value
37
+ # end
38
+ #
39
+ # by calling simple_option :name
40
+ #
41
+ def simple_option(option_name, default: nil)
42
+ @default_config ||= {}
43
+ @default_config[option_name.to_sym] = default
44
+
45
+ define_method(option_name) do |value = nil, &block|
46
+ config[option_name.to_sym] = block || value
47
+ end
48
+ end
49
+
50
+ ###
51
+ # Define a DSL method
52
+ #
53
+ # def render(name, &)
54
+ # config[:render] = Mensa::RenderDsl.new(name, &).config
55
+ # end
56
+ #
57
+ # by calling define_dsl :render, Mensa::RenderDsl
58
+ #
59
+ def dsl_option(option_name, klass)
60
+ define_method(option_name) do |name = nil, &block|
61
+ config[option_name.to_sym] = block && klass.new(name, &block).config
62
+ end
63
+ end
64
+
65
+ def dsl_hash(option_name, klass, config_name:)
66
+ config_name = config_name || option_name.to_s.pluralize.to_sym
67
+
68
+ @default_config ||= {}
69
+ @default_config[option_name.to_sym] = {}
70
+
71
+ define_method(option_name) do |name = nil, &block|
72
+ config[config_name] ||= {}
73
+ config[config_name][name] = klass.new(nil, &block).config
74
+ end
75
+ end
76
+
77
+ def dsl_array(option_name, klass, config_name: option_name.to_s.pluralize.to_sym)
78
+ config_name = config_name || option_name.to_s.pluralize.to_sym
79
+
80
+ @default_config ||= {}
81
+ @default_config[name.to_sym] = []
82
+
83
+ define_method(option_name) do |name = nil, &block|
84
+ config[config_name] ||= []
85
+ config[config_name] << klass.new(name, &block).config
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,8 @@
1
+ module Mensa::Config
2
+ class FilterDsl
3
+ include DslLogic
4
+
5
+ option :collection
6
+ option :scope
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ module Mensa::Config
2
+ class RenderDsl
3
+ include DslLogic
4
+
5
+ option :html
6
+ option :xlsx
7
+ end
8
+ end
@@ -0,0 +1,71 @@
1
+ # class UsersTable < Mensa::Base
2
+ # definition do
3
+ # model User
4
+ #
5
+ # render do # default Standard components
6
+ # html # Mensa::TableComponent::Default
7
+ # json # Mensa::JsonRenderer::Default
8
+ # xlsx # Mensa::XlsxRenderer::Default
9
+ # end
10
+ #
11
+ # column(:first_name) do
12
+ # filter
13
+ # # render do
14
+ # # html do |c|
15
+ # # link_to(edit_contact_path(c)) do
16
+ # # content_tag("i", nil, class: "fal fa-book")
17
+ # # end
18
+ # # end
19
+ # # end
20
+ # end
21
+ # column :last_name do
22
+ # filter
23
+ # end
24
+ # column :email
25
+ # column :phone_number
26
+ # column :state
27
+ # column :city
28
+ # column :created_at
29
+ #
30
+ # order last_name: :asc
31
+ # link { |user| edit_user_path(user) }
32
+ #
33
+ # supports_views true
34
+ #
35
+ # action :activate do
36
+ # link { |user| edit_user_path(user) }
37
+ # icon "fa-check"
38
+ # end
39
+ # action :delete do
40
+ # link { |user| edit_user_path(user) }
41
+ # link_attributes "data-turbo-method" => "delete"
42
+ # icon "fa-xmark"
43
+ # end
44
+ # end
45
+
46
+ module Mensa::Config
47
+ class TableDsl
48
+ include DslLogic
49
+
50
+ option :model, default: -> { self.class.name.demodulize.to_s.classify.constantize rescue raise "No model found for #{self.class.name}" }
51
+ option :column, dsl_hash: Mensa::Config::ColumnDsl
52
+ option :link
53
+
54
+ # Default sort order {column: direction, column: direction}
55
+ option :order, default: {}
56
+
57
+ # Order of columns in the table
58
+ option :column_order
59
+
60
+ # Actions
61
+ option :action, dsl_hash: Mensa::Config::ActionDsl
62
+
63
+ dsl_option :render, Mensa::Config::RenderDsl
64
+
65
+ option :supports_views, default: false
66
+ option :show_header, default: true
67
+ option :view_columns_sorting, default: true
68
+ option :view_condensed, default: false
69
+ option :view_condensed_toggle, default: true
70
+ end
71
+ end
@@ -0,0 +1,13 @@
1
+ module Mensa
2
+ module ConfigReaders
3
+ extend ActiveSupport::Concern
4
+
5
+ class_methods do
6
+ def config_reader(name)
7
+ define_method name do
8
+ config[name.to_s.gsub("?", "").to_sym]
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mensa
4
+ class Filter
5
+ include ConfigReaders
6
+ attr_reader :column, :value, :config, :table
7
+
8
+ config_reader :scope
9
+
10
+ def initialize(value, column:, config:, table:)
11
+ @value = value
12
+ @column = column
13
+ @config = config
14
+ @table = table
15
+ end
16
+
17
+ def collection
18
+ return unless config&.key?(:collection)
19
+
20
+ if config[:collection].is_a? Proc
21
+ table.original_view_context.instance_exec(&config[:collection])
22
+ else
23
+ config[:collection]
24
+ end
25
+ end
26
+
27
+ def to_s
28
+ "#{column.human_name}: #{value}"
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mensa
4
+ class Row
5
+ attr_reader :table, :view_context, :record
6
+
7
+ def initialize(table, record)
8
+ @table = table
9
+ @record = record
10
+ end
11
+
12
+ def value(column)
13
+ if column.attribute && record.respond_to?(column.attribute)
14
+ record.public_send(column.attribute)
15
+ elsif column.method && record.respond_to?(column.method)
16
+ record.public_send(column.method)
17
+ elsif record.respond_to?(column.name)
18
+ record.public_send(column.name)
19
+ elsif record.respond_to?(:[])
20
+ record[column.name]
21
+ else
22
+ "-"
23
+ end
24
+ end
25
+
26
+ def link_attributes
27
+ return {} unless link
28
+
29
+ { href: link, data: { controller: "satis-link", action: "click->satis-link#follow tap->satis-link#follow" } }
30
+ end
31
+
32
+ def link
33
+ table.original_view_context.instance_exec(record, &table.link) if table.link
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mensa
4
+ module Scope
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ end
9
+
10
+ # Returns the records we want to display, using the Active Record Query Interface
11
+ # By default it returns all records
12
+ def scope
13
+ model.all
14
+ end
15
+
16
+ # Returns the scope, but filtered
17
+ def filtered_scope
18
+ return @filtered_scope if @filtered_scope
19
+
20
+ @filtered_scope = scope
21
+ # See https://github.com/textacular/textacular
22
+ # This has problems - not all table fields are searched
23
+ @filtered_scope = @filtered_scope.basic_search(params[:query]) if params[:query]
24
+
25
+ if params[:filters]
26
+ params[:filters].each do |column_name, value|
27
+ next unless (column = column(column_name))
28
+
29
+ @filtered_scope = if column.filter&.scope
30
+ @filtered_scope.instance_exec(Helper.normalize(value), &column.filter.scope)
31
+ else
32
+ @filtered_scope.where(column.attribute_for_condition => Helper.normalize(value))
33
+ end
34
+ end
35
+ end
36
+
37
+ @filtered_scope
38
+ end
39
+
40
+ # Returns the filtered_columns, but ordered, we always reorder, scope shouldn't include ordering
41
+ def ordered_scope
42
+ return @ordered_scope if @ordered_scope
43
+
44
+ @ordered_scope = filtered_scope
45
+ @ordered_scope = @ordered_scope.reorder(order_hash)
46
+
47
+ @ordered_scope
48
+ end
49
+
50
+ # Return the ordered_scope, but with only the columns selected
51
+ def selected_scope
52
+ return @selected_scope if @selected_scope
53
+
54
+ @selected_scope = ordered_scope
55
+ @selected_scope = @selected_scope.select([:id] + columns.map(&:attribute).compact)
56
+
57
+ Rails.logger.debug("*" * 80)
58
+ Rails.logger.debug("Selected scope")
59
+ Rails.logger.debug(@selected_scope.to_sql)
60
+ Rails.logger.debug("*" * 80)
61
+
62
+
63
+ @selected_scope
64
+ end
65
+
66
+ def paged_scope
67
+ pagy_object
68
+ @records
69
+ end
70
+
71
+ def pagy_details
72
+ pagy_object
73
+ @pagy_details
74
+ end
75
+
76
+ private
77
+
78
+ def pagy_object
79
+ return if @pagy_details && @records
80
+
81
+ @pagy_details, @records = selected_scope.is_a?(Array) ? pagy_array(ordered_scope) : pagy(selected_scope)
82
+ end
83
+
84
+ # Though this works, perhaps moving this in column(s) is nicer
85
+ def order_hash(new_params = {})
86
+ (params[:order] || config[:order]).merge(new_params.symbolize_keys)
87
+ .reject { |name, direction| direction.blank? }
88
+ .transform_values { |value| value.to_sym }
89
+ end
90
+
91
+ module Helper
92
+ class << self
93
+ def normalize(query)
94
+ query.to_s.gsub(/\s(?![\&\!\|])/, '\\\\ ')
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,11 @@
1
+ doctype html
2
+ html
3
+ head
4
+ title Dummy
5
+ meta name="viewport" content="width=device-width,initial-scale=1"
6
+ = csrf_meta_tags
7
+ = csp_meta_tag
8
+ = stylesheet_link_tag "mensa"
9
+ = javascript_importmap_tags "mensa"
10
+ body
11
+ = yield
@@ -0,0 +1,8 @@
1
+ = turbo_stream.update params[:target] do
2
+ = sts.form_for(Mensa::Tables::FiltersController::Filter.new, url: '#') do |f|
3
+ - if @column.filter.collection
4
+ = f.input :value, collection: @column.filter.collection, input_html: { "data-action"=> "keydown.esc->mensa-add-filter#reset keydown.enter->mensa-add-filter#filterValueEntered", autofocus: "autofocus" }, label: @column.human_name
5
+ - else
6
+ = f.input :value, input_html: { "data-action"=> "keydown.esc->mensa-add-filter#reset keydown.enter->mensa-add-filter#filterValueEntered", autofocus: "autofocus" }, label: @column.human_name
7
+ a.sts-table__add_filter__popover_container__clear
8
+ | Clear
@@ -0,0 +1,93 @@
1
+ button data-controller="alert" data-action="alert#alert"
2
+ | Click me
3
+
4
+ <ul role="list" class="divide-y divide-gray-100">
5
+ <li class="flex justify-between gap-x-6 py-5">
6
+ <div class="flex min-w-0 gap-x-4">
7
+ <img class="h-12 w-12 flex-none rounded-full bg-gray-50" src="https://images.unsplash.com/photo-1494790108377-be9c29b29330?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt="">
8
+ <div class="min-w-0 flex-auto">
9
+ <p class="text-sm font-semibold leading-6 text-gray-900">Leslie Alexander</p>
10
+ <p class="mt-1 truncate text-xs leading-5 text-gray-500">leslie.alexander@example.com</p>
11
+ </div>
12
+ </div>
13
+ <div class="hidden shrink-0 sm:flex sm:flex-col sm:items-end">
14
+ <p class="text-sm leading-6 text-gray-900">Co-Founder / CEO</p>
15
+ <p class="mt-1 text-xs leading-5 text-gray-500">Last seen <time datetime="2023-01-23T13:23Z">3h ago</time></p>
16
+ </div>
17
+ </li>
18
+ <li class="flex justify-between gap-x-6 py-5">
19
+ <div class="flex min-w-0 gap-x-4">
20
+ <img class="h-12 w-12 flex-none rounded-full bg-gray-50" src="https://images.unsplash.com/photo-1519244703995-f4e0f30006d5?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt="">
21
+ <div class="min-w-0 flex-auto">
22
+ <p class="text-sm font-semibold leading-6 text-gray-900">Michael Foster</p>
23
+ <p class="mt-1 truncate text-xs leading-5 text-gray-500">michael.foster@example.com</p>
24
+ </div>
25
+ </div>
26
+ <div class="hidden shrink-0 sm:flex sm:flex-col sm:items-end">
27
+ <p class="text-sm leading-6 text-gray-900">Co-Founder / CTO</p>
28
+ <p class="mt-1 text-xs leading-5 text-gray-500">Last seen <time datetime="2023-01-23T13:23Z">3h ago</time></p>
29
+ </div>
30
+ </li>
31
+ <li class="flex justify-between gap-x-6 py-5">
32
+ <div class="flex min-w-0 gap-x-4">
33
+ <img class="h-12 w-12 flex-none rounded-full bg-gray-50" src="https://images.unsplash.com/photo-1506794778202-cad84cf45f1d?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt="">
34
+ <div class="min-w-0 flex-auto">
35
+ <p class="text-sm font-semibold leading-6 text-gray-900">Dries Vincent</p>
36
+ <p class="mt-1 truncate text-xs leading-5 text-gray-500">dries.vincent@example.com</p>
37
+ </div>
38
+ </div>
39
+ <div class="hidden shrink-0 sm:flex sm:flex-col sm:items-end">
40
+ <p class="text-sm leading-6 text-gray-900">Business Relations</p>
41
+ <div class="mt-1 flex items-center gap-x-1.5">
42
+ <div class="flex-none rounded-full bg-emerald-500/20 p-1">
43
+ <div class="h-1.5 w-1.5 rounded-full bg-emerald-500"></div>
44
+ </div>
45
+ <p class="text-xs leading-5 text-gray-500">Online</p>
46
+ </div>
47
+ </div>
48
+ </li>
49
+ <li class="flex justify-between gap-x-6 py-5">
50
+ <div class="flex min-w-0 gap-x-4">
51
+ <img class="h-12 w-12 flex-none rounded-full bg-gray-50" src="https://images.unsplash.com/photo-1517841905240-472988babdf9?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt="">
52
+ <div class="min-w-0 flex-auto">
53
+ <p class="text-sm font-semibold leading-6 text-gray-900">Lindsay Walton</p>
54
+ <p class="mt-1 truncate text-xs leading-5 text-gray-500">lindsay.walton@example.com</p>
55
+ </div>
56
+ </div>
57
+ <div class="hidden shrink-0 sm:flex sm:flex-col sm:items-end">
58
+ <p class="text-sm leading-6 text-gray-900">Front-end Developer</p>
59
+ <p class="mt-1 text-xs leading-5 text-gray-500">Last seen <time datetime="2023-01-23T13:23Z">3h ago</time></p>
60
+ </div>
61
+ </li>
62
+ <li class="flex justify-between gap-x-6 py-5">
63
+ <div class="flex min-w-0 gap-x-4">
64
+ <img class="h-12 w-12 flex-none rounded-full bg-gray-50" src="https://images.unsplash.com/photo-1438761681033-6461ffad8d80?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt="">
65
+ <div class="min-w-0 flex-auto">
66
+ <p class="text-sm font-semibold leading-6 text-gray-900">Courtney Henry</p>
67
+ <p class="mt-1 truncate text-xs leading-5 text-gray-500">courtney.henry@example.com</p>
68
+ </div>
69
+ </div>
70
+ <div class="hidden shrink-0 sm:flex sm:flex-col sm:items-end">
71
+ <p class="text-sm leading-6 text-gray-900">Designer</p>
72
+ <p class="mt-1 text-xs leading-5 text-gray-500">Last seen <time datetime="2023-01-23T13:23Z">3h ago</time></p>
73
+ </div>
74
+ </li>
75
+ <li class="flex justify-between gap-x-6 py-5">
76
+ <div class="flex min-w-0 gap-x-4">
77
+ <img class="h-12 w-12 flex-none rounded-full bg-gray-50" src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt="">
78
+ <div class="min-w-0 flex-auto">
79
+ <p class="text-sm font-semibold leading-6 text-gray-900">Tom Cook</p>
80
+ <p class="mt-1 truncate text-xs leading-5 text-gray-500">tom.cook@example.com</p>
81
+ </div>
82
+ </div>
83
+ <div class="hidden shrink-0 sm:flex sm:flex-col sm:items-end">
84
+ <p class="text-sm leading-6 text-gray-900">Director of Product</p>
85
+ <div class="mt-1 flex items-center gap-x-1.5">
86
+ <div class="flex-none rounded-full bg-emerald-500/20 p-1">
87
+ <div class="h-1.5 w-1.5 rounded-full bg-emerald-500"></div>
88
+ </div>
89
+ <p class="text-xs leading-5 text-gray-500">Online</p>
90
+ </div>
91
+ </div>
92
+ </li>
93
+ </ul>
@@ -0,0 +1,5 @@
1
+ /= turbo_stream.update "filters-#{@table.table_id}" do
2
+ = render Mensa::Filters::Component.new(table: @table)
3
+
4
+ turbo-frame id=@table.table_id target="_top"
5
+ = render Mensa::View::Component.new(@table)
@@ -0,0 +1,5 @@
1
+ = turbo_stream.update "filters-#{@table.table_id}" do
2
+ = render Mensa::Filters::Component.new(table: @table)
3
+
4
+ = turbo_stream.update @table.table_id do
5
+ = render Mensa::View::Component.new(@table)
data/bin/importmap ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative "../test/dummy/config/application"
4
+ require "importmap/commands"
data/bin/rails ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails gems
3
+ # installed from the root of your application.
4
+
5
+ ENGINE_ROOT = File.expand_path("..", __dir__)
6
+ ENGINE_PATH = File.expand_path("../lib/mensa/engine", __dir__)
7
+ APP_PATH = File.expand_path("../test/dummy/config/application", __dir__)
8
+
9
+ # Set up gems listed in the Gemfile.
10
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
11
+ require "bundler/setup" if File.exist?(ENV["BUNDLE_GEMFILE"])
12
+
13
+ require "rails/all"
14
+ require "rails/engine/commands"
@@ -0,0 +1,8 @@
1
+ pin "mensa", to: "mensa/application.js", preload: true
2
+ pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true
3
+ pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true
4
+ pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
5
+ pin '@fortawesome/fontawesome-free', to: 'https://ga.jspm.io/npm:@fortawesome/fontawesome-free@6.1.1/js/all.js'
6
+ pin_all_from Mensa::Engine.root.join("app/javascript/mensa/controllers"), under: "mensa/controllers", to: "mensa/controllers", preload: false
7
+ pin_all_from Mensa::Engine.root.join("app/components/mensa"), under: "mensa/components", to: "mensa", preload: false
8
+ pin "@rails/request.js", to: "@rails--request.js.js" # @0.0.9
@@ -0,0 +1,8 @@
1
+ en:
2
+ mensa:
3
+ views:
4
+ all: All
5
+ search:
6
+ component:
7
+ save: Save
8
+ cancel: Cancel
@@ -0,0 +1,11 @@
1
+ nl:
2
+ mensa:
3
+ add_filter:
4
+ component:
5
+ add_filter: Filter toevoegen
6
+ views:
7
+ all: Alles
8
+ search:
9
+ component:
10
+ save: Bewaar
11
+ cancel: Annuleer
data/config/routes.rb ADDED
@@ -0,0 +1,7 @@
1
+ Mensa::Engine.routes.draw do
2
+ resources :tables do
3
+ scope module: :tables do
4
+ resources :filters
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,15 @@
1
+ class CreateMensaTableViews < ActiveRecord::Migration[7.1]
2
+ def change
3
+ create_table :mensa_table_views, id: :uuid do |t|
4
+ t.string :table_name
5
+ t.string :name
6
+ t.jsonb :data
7
+
8
+ t.references :user, null: true, foreign_key: true, type: :uuid
9
+
10
+ t.timestamps
11
+ end
12
+ add_index :mensa_table_views, :table_name, unique: false
13
+ add_index :mensa_table_views, :data, using: :gin
14
+ end
15
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mensa
4
+ class InstallGenerator < Rails::Generators::Base
5
+ source_root File.expand_path("../templates", __FILE__)
6
+
7
+ def create_initializer_file
8
+ template "config/initializers/mensa.rb"
9
+ end
10
+
11
+ def add_route
12
+ return if Rails.application.routes.routes.detect { |route| route.app.app == Mensa::Engine }
13
+ route %(mount Mensa::Engine => "/mensa")
14
+ end
15
+
16
+ def copy_migrations
17
+ rake "mensa:install:migrations"
18
+ end
19
+
20
+ def tailwindcss_config
21
+ rake "mensa:tailwindcss:config"
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ require 'rails/generators/base'
2
+
3
+ module Mensa
4
+ module Generators
5
+ class TailwindConfigGenerator < Rails::Generators::Base
6
+ source_root File.expand_path('../templates', __dir__)
7
+ desc 'Configures tailwind.config.js and application.tailwindcss.css'
8
+
9
+ def add_content_to_tailwind_config
10
+ inject_into_file "config/tailwind.config.js", before: "],\n theme: {" do
11
+ " // Mensa content\n" +
12
+ %w[/app/views/**/* /app/helpers/**/* /app/controllers/**/* /app/components/**/* /app/javascript/**/*.js /app/assets/**/*.css].map { |path| " \"#{Mensa::Engine.root}#{path}\"" }.join(",\n") +
13
+ ",\n "
14
+ end
15
+ end
16
+
17
+ def add_content_application_tailwind_css
18
+ inject_into_file "app/assets/stylesheets/application.tailwind.css", before: "@tailwind base;" do
19
+ "@import '#{Mensa::Engine.root}/app/assets/stylesheets/mensa/application.css';\n"
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,22 @@
1
+ Mensa.setup do |config|
2
+ # Callbacks for exporting of tables
3
+ config.callbacks = {
4
+ export_started: lambda do |user_id, table_name|
5
+ end,
6
+ export_completed: lambda do |user_id, table_name, context, attachment|
7
+ end
8
+ }
9
+
10
+ # Override icons in use
11
+ config.icons = {
12
+ order_indicator_asc: "fa-solid fa-sort-up",
13
+ order_indicator_desc: "fa-solid fa-sort-down",
14
+ order_indicator: "fa-solid fa-sort",
15
+ control_bar_search: "fa-solid fa-magnifying-glass",
16
+ control_bar_filter: "fa-solid fa-filter",
17
+ control_bar_edit: "fa-solid fa-table-columns",
18
+ control_bar_compress: "fa-solid fa-compress",
19
+ control_bar_export: "fa-solid fa-file-export",
20
+ search: "fa-solid fa-magnifying-glass",
21
+ }
22
+ end