mensa 0.2.5 → 0.2.6

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 (98) hide show
  1. checksums.yaml +4 -4
  2. data/.devcontainer/Dockerfile +6 -2
  3. data/.devcontainer/compose.yaml +1 -1
  4. data/.devcontainer/devcontainer.json +31 -29
  5. data/.devcontainer/postCreate.sh +8 -0
  6. data/.devcontainer/postStart.sh +9 -0
  7. data/.gitignore +3 -1
  8. data/.zed/tasks.json +12 -0
  9. data/Gemfile.lock +155 -153
  10. data/Procfile +1 -1
  11. data/README.md +85 -60
  12. data/app/assets/stylesheets/mensa/application.css +14 -11
  13. data/app/components/mensa/add_filter/component.css +110 -5
  14. data/app/components/mensa/add_filter/component.html.slim +10 -12
  15. data/app/components/mensa/add_filter/component.rb +7 -1
  16. data/app/components/mensa/add_filter/component_controller.js +697 -83
  17. data/app/components/mensa/cell/component.css +9 -0
  18. data/app/components/mensa/column_customizer/component.css +40 -0
  19. data/app/components/mensa/column_customizer/component.html.slim +14 -0
  20. data/app/components/mensa/column_customizer/component.rb +13 -0
  21. data/app/components/mensa/column_customizer/component_controller.js +383 -0
  22. data/app/components/mensa/control_bar/component.css +127 -4
  23. data/app/components/mensa/control_bar/component.html.slim +41 -14
  24. data/app/components/mensa/control_bar/component.rb +0 -4
  25. data/app/components/mensa/empty_state/component.css +20 -0
  26. data/app/components/mensa/empty_state/component.html.slim +7 -0
  27. data/app/components/mensa/empty_state/component.rb +18 -0
  28. data/app/components/mensa/filter_pill/component.css +23 -0
  29. data/app/components/mensa/filter_pill/component.html.slim +9 -6
  30. data/app/components/mensa/filter_pill/component.rb +9 -0
  31. data/app/components/mensa/filter_pill/component_controller.js +50 -10
  32. data/app/components/mensa/filter_pill_list/component.css +58 -9
  33. data/app/components/mensa/filter_pill_list/component.html.slim +11 -8
  34. data/app/components/mensa/filter_pill_list/component_controller.js +747 -48
  35. data/app/components/mensa/header/component.css +41 -43
  36. data/app/components/mensa/header/component.html.slim +7 -7
  37. data/app/components/mensa/row_action/component.html.slim +2 -2
  38. data/app/components/mensa/search/component.css +68 -9
  39. data/app/components/mensa/search/component.html.slim +19 -15
  40. data/app/components/mensa/search/component_controller.js +39 -49
  41. data/app/components/mensa/selection/component_controller.js +147 -0
  42. data/app/components/mensa/table/component.css +28 -0
  43. data/app/components/mensa/table/component.html.slim +9 -6
  44. data/app/components/mensa/table/component.rb +1 -0
  45. data/app/components/mensa/table/component_controller.js +524 -88
  46. data/app/components/mensa/table_row/component.css +6 -0
  47. data/app/components/mensa/table_row/component.html.slim +8 -3
  48. data/app/components/mensa/view/component.css +97 -29
  49. data/app/components/mensa/view/component.html.slim +23 -10
  50. data/app/components/mensa/view/component.rb +5 -0
  51. data/app/components/mensa/views/component.css +106 -13
  52. data/app/components/mensa/views/component.html.slim +51 -17
  53. data/app/components/mensa/views/component_controller.js +245 -20
  54. data/app/controllers/mensa/tables/batch_actions_controller.rb +24 -0
  55. data/app/controllers/mensa/tables/exports_controller.rb +96 -0
  56. data/app/controllers/mensa/tables/filters_controller.rb +4 -1
  57. data/app/controllers/mensa/tables/views_controller.rb +108 -0
  58. data/app/controllers/mensa/tables_controller.rb +3 -6
  59. data/app/helpers/mensa/application_helper.rb +4 -0
  60. data/app/javascript/mensa/application.js +2 -2
  61. data/app/javascript/mensa/controllers/index.js +13 -4
  62. data/app/jobs/mensa/export_job.rb +77 -84
  63. data/app/models/mensa/export.rb +93 -0
  64. data/app/tables/mensa/base.rb +103 -12
  65. data/app/tables/mensa/batch_action.rb +27 -0
  66. data/app/tables/mensa/cell.rb +15 -0
  67. data/app/tables/mensa/column.rb +15 -2
  68. data/app/tables/mensa/config/batch_dsl.rb +13 -0
  69. data/app/tables/mensa/config/column_dsl.rb +1 -0
  70. data/app/tables/mensa/config/filter_dsl.rb +4 -1
  71. data/app/tables/mensa/config/render_dsl.rb +1 -1
  72. data/app/tables/mensa/config/table_dsl.rb +12 -5
  73. data/app/tables/mensa/config/view_dsl.rb +2 -0
  74. data/app/tables/mensa/config_readers.rb +20 -1
  75. data/app/tables/mensa/filter.rb +86 -3
  76. data/app/tables/mensa/scope.rb +24 -12
  77. data/app/views/mensa/exports/_badge.html.slim +5 -0
  78. data/app/views/mensa/exports/_dialog.html.slim +42 -0
  79. data/app/views/mensa/exports/_list.html.slim +29 -0
  80. data/app/views/mensa/tables/filters/show.turbo_stream.slim +35 -8
  81. data/app/views/mensa/tables/views/create.turbo_stream.slim +11 -0
  82. data/app/views/mensa/tables/views/destroy.turbo_stream.slim +11 -0
  83. data/app/views/mensa/tables/views/update.turbo_stream.slim +11 -0
  84. data/config/locales/en.yml +44 -0
  85. data/config/locales/nl.yml +45 -0
  86. data/config/routes.rb +7 -0
  87. data/db/migrate/20260604120000_create_mensa_exports.rb +25 -0
  88. data/docs/columns.png +0 -0
  89. data/docs/export.png +0 -0
  90. data/docs/filters.png +0 -0
  91. data/docs/table.png +0 -0
  92. data/lib/mensa/configuration.rb +33 -12
  93. data/lib/mensa/engine.rb +7 -2
  94. data/lib/mensa/version.rb +1 -1
  95. data/mensa.gemspec +2 -1
  96. data/mise.toml +8 -0
  97. data/package-lock.json +0 -7
  98. metadata +50 -8
@@ -1,9 +1,36 @@
1
1
  = turbo_stream.update params[:target] do
2
- = sts.form_for(Mensa::Tables::FiltersController::Filter.new, url: '#') do |f|
3
- = 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", "data-mensa-add-filter-target" => "value" }, label: @column.human_name
4
- button.text-xs.ring-gray-300.ring-inset.ring-1.rounded.px-1.py-0.5 Esc
5
- span.text-xs.pl-1.pr-2
6
- | reset
7
- button.text-xs.ring-gray-300.ring-inset.ring-1.rounded.px-1.py-0.5 Enter
8
- span.text-xs.pl-1.pr-2
9
- | submit
2
+ .mensa-table__add_filter__popover_container__heading
3
+ = @column.human_name
4
+ - if @column.filter.operator_with_value?
5
+ - collection = @column.filter.collection
6
+ - if collection.present?
7
+ - unless @multiple
8
+ input[type="hidden" data-mensa-add-filter-target="value" value=@values.first]
9
+ ul.mensa-table__add_filter__popover_container__values data-multiple=("true" if @multiple)
10
+ - collection.each do |item|
11
+ - opt_label = item.is_a?(Array) ? item.first.to_s : item.to_s
12
+ - opt_value = item.is_a?(Array) ? item.last.to_s : item.to_s
13
+ - is_selected = @values.include?(opt_value)
14
+ li.mensa-table__add_filter__popover_container__value[data-mensa-add-filter-target="valueOption" data-value=opt_value data-label=opt_label data-selected=("true" if is_selected) data-action="click->mensa-add-filter#selectValue mouseenter->mensa-add-filter#highlightItem"]
15
+ - if @multiple
16
+ span.mensa-table__add_filter__checkbox class=("mensa-table__add_filter__checkbox--checked" if is_selected)
17
+ - else
18
+ i.mensa-table__add_filter__popover_container__value__check.fa-solid.fa-check class=("invisible" unless is_selected)
19
+ span.flex-1
20
+ = opt_label
21
+ span.mensa-table__add_filter__enter-hint
22
+ | ↵ Enter
23
+ - else
24
+ input.mensa-table__add_filter__popover_container__input[type=@column.filter.input_type value=@values.first autocomplete="off" inputmode=(@column.type == :integer ? "numeric" : nil) step=(@column.type == :integer ? 1 : (@column.type == :datetime ? 1 : nil)) data-mensa-add-filter-target="value" data-action="input->mensa-add-filter#manualValueChanged keydown.enter->mensa-add-filter#applyManualValue"]
25
+ hr.mensa-table__add_filter__popover_container__separator
26
+ ul.mensa-table__add_filter__popover_container__operators
27
+ - @column.filter.operators.each do |operator_name, label, requires_value|
28
+ li.mensa-table__add_filter__popover_container__operator[data-mensa-add-filter-target="operatorOption" data-operator=operator_name data-requires-value=(requires_value ? "true" : "false") data-selected=("true" if @operator == operator_name.to_s) data-action="click->mensa-add-filter#selectOperator mouseenter->mensa-add-filter#highlightItem"]
29
+ i.mensa-table__add_filter__popover_container__operator__check.fa-solid.fa-check class=("invisible" unless @operator == operator_name.to_s)
30
+ span.flex-1
31
+ = label
32
+ span.mensa-table__add_filter__enter-hint
33
+ | ↵ Enter
34
+ hr.mensa-table__add_filter__popover_container__separator
35
+ a.mensa-table__add_filter__popover_container__clear[href="#" data-action="click->mensa-add-filter#reset"]
36
+ | Clear
@@ -0,0 +1,11 @@
1
+ / Replace the views tabs so the newly saved view tab appears, selected.
2
+ = turbo_stream.replace "mensa-views-#{@table.table_id}" do
3
+ = render Mensa::Views::Component.new(table: @table)
4
+
5
+ / Clear the filter pills — the saved view starts fresh with no extra filters.
6
+ = turbo_stream.update "filters-#{@table.table_id}" do
7
+ = render Mensa::FilterPillList::Component.new(table: @table)
8
+
9
+ / Re-render the table body with the saved view's data.
10
+ = turbo_stream.update @table.table_id do
11
+ = render Mensa::View::Component.new(@table)
@@ -0,0 +1,11 @@
1
+ / Replace the views dropdown without the deleted view; @table is at the default view.
2
+ = turbo_stream.replace "mensa-views-#{@table.table_id}" do
3
+ = render Mensa::Views::Component.new(table: @table)
4
+
5
+ / Reset the search bar to the default view state.
6
+ = turbo_stream.update "filters-#{@table.table_id}" do
7
+ = render Mensa::FilterPillList::Component.new(table: @table)
8
+
9
+ / Re-render the table body with the default view data.
10
+ = turbo_stream.update @table.table_id do
11
+ = render Mensa::View::Component.new(@table)
@@ -0,0 +1,11 @@
1
+ / Replace the views tabs so the updated view tab stays selected.
2
+ = turbo_stream.replace "mensa-views-#{@table.table_id}" do
3
+ = render Mensa::Views::Component.new(table: @table)
4
+
5
+ / Clear the filter pills — the view now reflects the saved config.
6
+ = turbo_stream.update "filters-#{@table.table_id}" do
7
+ = render Mensa::FilterPillList::Component.new(table: @table)
8
+
9
+ / Re-render the table body with the updated view's data.
10
+ = turbo_stream.update @table.table_id do
11
+ = render Mensa::View::Component.new(@table)
@@ -1,11 +1,55 @@
1
1
  en:
2
2
  mensa:
3
+ operators:
4
+ is: is
5
+ isnt: isn't
6
+ matches: matches
7
+ does_not_match: doesn't match
8
+ is_current: is current
9
+ gt: greater than
10
+ lt: less than
11
+ gteq: greater than or equal to
12
+ lteq: less than or equal to
3
13
  add_filter:
4
14
  component:
5
15
  add_filter: Add filter
16
+ filter_pill_list:
17
+ component:
18
+ search: Search and filter
19
+ search_only: Search
6
20
  search:
7
21
  component:
22
+ search_in: Search in %{view}
8
23
  cancel: Cancel
9
24
  save: Save
25
+ save_view_title: Save view
26
+ save_view_subtitle: Save the current filters, ordering and search as a reusable view.
27
+ view_name: Name
28
+ view_name_placeholder: e.g. Active customers
29
+ view_description: Description
30
+ view_description_placeholder: Optional notes about this view
31
+ exports:
32
+ title: "Export %{table}"
33
+ close: Close
34
+ available_downloads: Available downloads
35
+ empty: You have no downloads yet. Create one below.
36
+ item_name: "%{table} export"
37
+ download: Download
38
+ processing: Preparing…
39
+ failed: Failed
40
+ new_export: New export
41
+ scope_all: All records (matching current filters)
42
+ scope_current_page: Current page
43
+ export_as: Export as
44
+ format_excel: CSV for Excel, Numbers, or other spreadsheet programs
45
+ format_plain: Plain CSV file
46
+ cancel: Cancel
47
+ submit: Export
48
+ paging:
49
+ info: Displaying %{model} %{from}-%{to} of %{count}
50
+ empty_state:
51
+ title: No %{model} found
52
+ subtitle: Try changing the filters or search terms for this view
53
+ clear_button: Clear search and filters
10
54
  views:
11
55
  default: All
@@ -1,11 +1,56 @@
1
1
  nl:
2
2
  mensa:
3
+ operators:
4
+ is: is
5
+ isnt: is niet
6
+ matches: matcht
7
+ does_not_match: matcht niet
8
+ is_current: is huidige
9
+ gt: groter dan
10
+ lt: kleiner dan
11
+ gteq: groter dan of gelijk aan
12
+ lteq: kleiner dan of gelijk aan
3
13
  add_filter:
4
14
  component:
5
15
  add_filter: Filter toevoegen
16
+ filter_pill_list:
17
+ component:
18
+ search: Zoeken en filteren
19
+ search_only: Zoeken
6
20
  search:
7
21
  component:
22
+ search: Zoek en filter
23
+ search_in: Zoek in %{view}
8
24
  cancel: Annuleer
9
25
  save: Bewaar
26
+ save_view_title: Weergave bewaren
27
+ save_view_subtitle: Bewaar de huidige filters, sortering en zoekopdracht als een herbruikbare weergave.
28
+ view_name: Naam
29
+ view_name_placeholder: bijv. Actieve klanten
30
+ view_description: Omschrijving
31
+ view_description_placeholder: Optionele notities over deze weergave
32
+ exports:
33
+ title: "%{table} exporteren"
34
+ close: Sluiten
35
+ available_downloads: Beschikbare downloads
36
+ empty: Je hebt nog geen downloads. Maak er hieronder een aan.
37
+ item_name: "%{table} export"
38
+ download: Downloaden
39
+ processing: Bezig…
40
+ failed: Mislukt
41
+ new_export: Nieuwe export
42
+ scope_all: Alle records (volgens huidige filters)
43
+ scope_current_page: Huidige pagina
44
+ export_as: Exporteren als
45
+ format_excel: CSV voor Excel, Numbers of andere spreadsheetprogramma's
46
+ format_plain: Eenvoudig CSV-bestand
47
+ cancel: Annuleer
48
+ submit: Exporteren
49
+ paging:
50
+ info: "%{model} %{from}-%{to} van %{count} weergeven"
51
+ empty_state:
52
+ title: Geen %{model} gevonden
53
+ subtitle: Probeer de filters of zoektermen voor deze weergave te wijzigen
54
+ clear_button: Zoekopdracht en filters wissen
10
55
  views:
11
56
  default: Alles
data/config/routes.rb CHANGED
@@ -2,6 +2,13 @@ Mensa::Engine.routes.draw do
2
2
  resources :tables do
3
3
  scope module: :tables do
4
4
  resources :filters
5
+ resources :views, only: [:create, :update, :destroy]
6
+ resources :batch_actions, only: [:create]
7
+ resources :exports, only: [:index, :create] do
8
+ member do
9
+ get :download
10
+ end
11
+ end
5
12
  end
6
13
  end
7
14
  end
@@ -0,0 +1,25 @@
1
+ class CreateMensaExports < ActiveRecord::Migration[7.1]
2
+ def change
3
+ create_table :mensa_exports, id: :uuid do |t|
4
+ t.string :table_name, null: false
5
+ # The view (system or custom) the export was generated for, if applicable.
6
+ t.references :table_view, null: true, type: :uuid, foreign_key: {to_table: :mensa_table_views}
7
+
8
+ # Scope ("all" / "current_page") and output format ("csv_excel" / "plain_csv").
9
+ t.string :scope
10
+ t.string :format
11
+ # Lifecycle: pending -> processing -> completed / failed.
12
+ t.string :status, null: false, default: "pending"
13
+ # The request configuration (filters, query, order, page) used to build the export.
14
+ t.jsonb :config, null: false, default: {}
15
+ t.string :filename
16
+
17
+ t.references :user, null: true, foreign_key: true, type: :uuid
18
+
19
+ t.timestamps
20
+ end
21
+
22
+ add_index :mensa_exports, :table_name
23
+ add_index :mensa_exports, :status
24
+ end
25
+ end
data/docs/columns.png ADDED
Binary file
data/docs/export.png ADDED
Binary file
data/docs/filters.png CHANGED
Binary file
data/docs/table.png CHANGED
Binary file
@@ -3,9 +3,20 @@
3
3
  module Mensa
4
4
  module Options
5
5
  module ClassMethods
6
- def option(name, default: nil)
7
- attr_accessor(name)
8
- schema[name] = default
6
+ def option(name, default: nil, proc: false)
7
+ attr_writer(name)
8
+ schema[name] = {default: default, proc: proc}
9
+
10
+ if schema[name][:proc]
11
+ define_method(name) do |*params|
12
+ value = instance_variable_get(:"@#{name}")
13
+ instance_exec(*params, &value)
14
+ end
15
+ else
16
+ define_method(name) do
17
+ instance_variable_get(:"@#{name}")
18
+ end
19
+ end
9
20
  end
10
21
 
11
22
  def schema
@@ -14,8 +25,8 @@ module Mensa
14
25
  end
15
26
 
16
27
  def set_defaults!
17
- self.class.schema.each do |name, default|
18
- instance_variable_set("@#{name}", default)
28
+ self.class.schema.each do |name, options|
29
+ instance_variable_set(:"@#{name}", options[:default])
19
30
  end
20
31
  end
21
32
 
@@ -32,9 +43,8 @@ module Mensa
32
43
 
33
44
  # TODO: add all icons below
34
45
  option :icons, default: {
35
- order_indicator_asc: "fa-solid fa-sort-up",
36
- order_indicator_desc: "fa-solid fa-sort-down",
37
- order_indicator: "fa-solid fa-sort",
46
+ order_indicator_asc: "fa-solid fa-arrow-up",
47
+ order_indicator_desc: "fa-solid fa-arrow-down",
38
48
  control_bar_search: "fa-solid fa-magnifying-glass",
39
49
  control_bar_filter: "fa-solid fa-filter",
40
50
  control_bar_edit: "fa-solid fa-table-columns",
@@ -57,14 +67,25 @@ module Mensa
57
67
  # }
58
68
 
59
69
  option :callbacks, default: {
60
- # Callback is called when the export is started,
61
- # the return value is passed as context to the export_complete callback
62
- export_started: lambda do |user_id, table_name|
70
+ # Called with the Mensa::Export when the export job starts processing.
71
+ export_started: lambda do |export|
63
72
  end,
64
- export_complete: lambda do |user_id, table_name, context, attachment|
73
+ # Called with the Mensa::Export once the CSV has been generated and
74
+ # attached (export.asset). Use this to e.g. notify or email the user.
75
+ export_complete: lambda do |export|
65
76
  end
66
77
  }
67
78
 
79
+ option :row_actions_position, default: :back
80
+ # It's either :basic or :fuzzy, for fuzzy search you need to have `pg_trgm` extension installed
81
+ option :search, default: -> {
82
+ @_search_cache ||= begin
83
+ (ActiveRecord::Base.connection.execute("SELECT extname FROM pg_extension where extname='pg_trgm';")&.first&.[]("extname") == "pg_trgm") ? :fuzzy : :basic
84
+ rescue
85
+ :basic
86
+ end
87
+ }, proc: true
88
+
68
89
  def initialize
69
90
  set_defaults!
70
91
  end
data/lib/mensa/engine.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  require "pagy"
2
2
  require "satis"
3
3
  require "textacular"
4
- require "caxlsx_rails"
5
4
  require "slim"
6
5
  require "tailwindcss-rails"
7
6
  require "importmap-rails"
@@ -28,8 +27,14 @@ module Mensa
28
27
 
29
28
  initializer "mensa.helper" do
30
29
  Rails.application.reloader.to_prepare do
30
+ ActiveSupport.on_load(:action_view) do
31
+ include Mensa::ApplicationHelper
32
+ end
31
33
  ActiveSupport.on_load(:action_controller) do
32
- Satis.add_helper(:table, ::Mensa::Table::Component)
34
+ # Only do this if Satis is defined, this will be deprecated!
35
+ if defined?(Satis)
36
+ Satis.add_helper(:table, ::Mensa::Table::Component)
37
+ end
33
38
  end
34
39
  end
35
40
  end
data/lib/mensa/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Mensa
2
- VERSION = "0.2.5"
2
+ VERSION = "0.2.6"
3
3
  end
data/mensa.gemspec CHANGED
@@ -29,8 +29,9 @@ Gem::Specification.new do |spec|
29
29
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
30
30
  end
31
31
 
32
- spec.add_dependency "caxlsx_rails", "~> 0"
33
32
  spec.add_dependency "rails", ">= 7.1"
33
+ spec.add_dependency "csv"
34
+ spec.add_dependency "rubyzip", ">= 1.2.2"
34
35
  spec.add_dependency "pagy", ">=43"
35
36
  spec.add_dependency "textacular", ">=5"
36
37
  spec.add_dependency "view_component", "~> 3.11"
data/mise.toml ADDED
@@ -0,0 +1,8 @@
1
+ [tools]
2
+ node = "24"
3
+ ruby = "3.4.7"
4
+
5
+ [env]
6
+ RUBY_VERSION="3.4.7"
7
+ # SELENIUM_URL="http://selenium:4444/"
8
+ SELENIUM_URL="http://host.docker.internal:4444/"
data/package-lock.json CHANGED
@@ -305,7 +305,6 @@
305
305
  "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
306
306
  "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
307
307
  "license": "MIT",
308
- "peer": true,
309
308
  "bin": {
310
309
  "acorn": "bin/acorn"
311
310
  },
@@ -1098,7 +1097,6 @@
1098
1097
  "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==",
1099
1098
  "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
1100
1099
  "license": "MIT",
1101
- "peer": true,
1102
1100
  "dependencies": {
1103
1101
  "@eslint-community/eslint-utils": "^4.2.0",
1104
1102
  "@eslint-community/regexpp": "^4.6.1",
@@ -1296,7 +1294,6 @@
1296
1294
  "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz",
1297
1295
  "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
1298
1296
  "license": "MIT",
1299
- "peer": true,
1300
1297
  "dependencies": {
1301
1298
  "@rtsao/scc": "^1.1.0",
1302
1299
  "array-includes": "^3.1.9",
@@ -1373,7 +1370,6 @@
1373
1370
  "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-15.7.0.tgz",
1374
1371
  "integrity": "sha512-jDex9s7D/Qial8AGVIHq4W7NswpUD5DPDL2RH8Lzd9EloWUuvUkHfv4FRLMipH5q2UtyurorBkPeNi1wVWNh3Q==",
1375
1372
  "license": "MIT",
1376
- "peer": true,
1377
1373
  "dependencies": {
1378
1374
  "builtins": "^5.0.1",
1379
1375
  "eslint-plugin-es": "^4.1.0",
@@ -1433,7 +1429,6 @@
1433
1429
  "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.6.0.tgz",
1434
1430
  "integrity": "sha512-57Zzfw8G6+Gq7axm2Pdo3gW/Rx3h9Yywgn61uE/3elTCOePEHVrn2i5CdfBwA1BLK0Q0WqctICIUSqXZW/VprQ==",
1435
1431
  "license": "ISC",
1436
- "peer": true,
1437
1432
  "engines": {
1438
1433
  "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
1439
1434
  },
@@ -1449,7 +1444,6 @@
1449
1444
  "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz",
1450
1445
  "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==",
1451
1446
  "license": "MIT",
1452
- "peer": true,
1453
1447
  "dependencies": {
1454
1448
  "array-includes": "^3.1.8",
1455
1449
  "array.prototype.findlast": "^1.2.5",
@@ -3451,7 +3445,6 @@
3451
3445
  }
3452
3446
  ],
3453
3447
  "license": "MIT",
3454
- "peer": true,
3455
3448
  "dependencies": {
3456
3449
  "nanoid": "^3.3.11",
3457
3450
  "picocolors": "^1.1.1",
metadata CHANGED
@@ -1,43 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mensa
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.5
4
+ version: 0.2.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tom de Grunt
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-11-21 00:00:00.000000000 Z
11
+ date: 2026-06-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: caxlsx_rails
14
+ name: rails
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '7.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '7.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: csv
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
18
32
  - !ruby/object:Gem::Version
19
33
  version: '0'
20
34
  type: :runtime
21
35
  prerelease: false
22
36
  version_requirements: !ruby/object:Gem::Requirement
23
37
  requirements:
24
- - - "~>"
38
+ - - ">="
25
39
  - !ruby/object:Gem::Version
26
40
  version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
- name: rails
42
+ name: rubyzip
29
43
  requirement: !ruby/object:Gem::Requirement
30
44
  requirements:
31
45
  - - ">="
32
46
  - !ruby/object:Gem::Version
33
- version: '7.1'
47
+ version: 1.2.2
34
48
  type: :runtime
35
49
  prerelease: false
36
50
  version_requirements: !ruby/object:Gem::Requirement
37
51
  requirements:
38
52
  - - ">="
39
53
  - !ruby/object:Gem::Version
40
- version: '7.1'
54
+ version: 1.2.2
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: pagy
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -175,10 +189,13 @@ files:
175
189
  - ".devcontainer/Dockerfile"
176
190
  - ".devcontainer/compose.yaml"
177
191
  - ".devcontainer/devcontainer.json"
192
+ - ".devcontainer/postCreate.sh"
193
+ - ".devcontainer/postStart.sh"
178
194
  - ".gem_release.yml"
179
195
  - ".github/workflows/gem-push.yml"
180
196
  - ".gitignore"
181
197
  - ".rubocop.yml"
198
+ - ".zed/tasks.json"
182
199
  - CHANGELOG.md
183
200
  - Gemfile
184
201
  - Gemfile.lock
@@ -197,9 +214,17 @@ files:
197
214
  - app/components/mensa/cell/component.css
198
215
  - app/components/mensa/cell/component.html.slim
199
216
  - app/components/mensa/cell/component.rb
217
+ - app/components/mensa/column_customizer/component.css
218
+ - app/components/mensa/column_customizer/component.html.slim
219
+ - app/components/mensa/column_customizer/component.rb
220
+ - app/components/mensa/column_customizer/component_controller.js
200
221
  - app/components/mensa/control_bar/component.css
201
222
  - app/components/mensa/control_bar/component.html.slim
202
223
  - app/components/mensa/control_bar/component.rb
224
+ - app/components/mensa/empty_state/component.css
225
+ - app/components/mensa/empty_state/component.html.slim
226
+ - app/components/mensa/empty_state/component.rb
227
+ - app/components/mensa/filter_pill/component.css
203
228
  - app/components/mensa/filter_pill/component.html.slim
204
229
  - app/components/mensa/filter_pill/component.rb
205
230
  - app/components/mensa/filter_pill/component_controller.js
@@ -217,6 +242,7 @@ files:
217
242
  - app/components/mensa/search/component.html.slim
218
243
  - app/components/mensa/search/component.rb
219
244
  - app/components/mensa/search/component_controller.js
245
+ - app/components/mensa/selection/component_controller.js
220
246
  - app/components/mensa/table/component.css
221
247
  - app/components/mensa/table/component.html.slim
222
248
  - app/components/mensa/table/component.rb
@@ -234,7 +260,10 @@ files:
234
260
  - app/components/mensa/views/component_controller.js
235
261
  - app/controllers/concerns/.keep
236
262
  - app/controllers/mensa/application_controller.rb
263
+ - app/controllers/mensa/tables/batch_actions_controller.rb
264
+ - app/controllers/mensa/tables/exports_controller.rb
237
265
  - app/controllers/mensa/tables/filters_controller.rb
266
+ - app/controllers/mensa/tables/views_controller.rb
238
267
  - app/controllers/mensa/tables_controller.rb
239
268
  - app/helpers/mensa/application_helper.rb
240
269
  - app/helpers/mensa/tables_helper.rb
@@ -247,12 +276,15 @@ files:
247
276
  - app/jobs/mensa/export_job.rb
248
277
  - app/models/concerns/.keep
249
278
  - app/models/mensa/application_record.rb
279
+ - app/models/mensa/export.rb
250
280
  - app/models/mensa/table_view.rb
251
281
  - app/tables/mensa/action.rb
252
282
  - app/tables/mensa/base.rb
283
+ - app/tables/mensa/batch_action.rb
253
284
  - app/tables/mensa/cell.rb
254
285
  - app/tables/mensa/column.rb
255
286
  - app/tables/mensa/config/action_dsl.rb
287
+ - app/tables/mensa/config/batch_dsl.rb
256
288
  - app/tables/mensa/config/column_dsl.rb
257
289
  - app/tables/mensa/config/dsl_logic.rb
258
290
  - app/tables/mensa/config/filter_dsl.rb
@@ -264,9 +296,15 @@ files:
264
296
  - app/tables/mensa/row.rb
265
297
  - app/tables/mensa/scope.rb
266
298
  - app/tables/mensa/system_view.rb
299
+ - app/views/mensa/exports/_badge.html.slim
300
+ - app/views/mensa/exports/_dialog.html.slim
301
+ - app/views/mensa/exports/_list.html.slim
267
302
  - app/views/mensa/tables/filters/show.turbo_stream.slim
268
303
  - app/views/mensa/tables/show.html.slim
269
304
  - app/views/mensa/tables/show.turbo_stream.slim
305
+ - app/views/mensa/tables/views/create.turbo_stream.slim
306
+ - app/views/mensa/tables/views/destroy.turbo_stream.slim
307
+ - app/views/mensa/tables/views/update.turbo_stream.slim
270
308
  - bin/importmap
271
309
  - bin/overmind
272
310
  - bin/rails
@@ -277,6 +315,9 @@ files:
277
315
  - config/routes.rb
278
316
  - db/migrate/20240201184752_create_mensa_table_views.rb
279
317
  - db/migrate/20251112143558_add_description_to_table_view.rb
318
+ - db/migrate/20260604120000_create_mensa_exports.rb
319
+ - docs/columns.png
320
+ - docs/export.png
280
321
  - docs/filters.png
281
322
  - docs/table.png
282
323
  - lib/generators/mensa/install_generator.rb
@@ -288,6 +329,7 @@ files:
288
329
  - lib/mensa/version.rb
289
330
  - lib/tasks/mensa_tasks.rake
290
331
  - mensa.gemspec
332
+ - mise.toml
291
333
  - package-lock.json
292
334
  - package.json
293
335
  - vendor/javascript/@rails--request.js.js