mensa 0.3.2 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/Gemfile.lock +1 -17
  4. data/README.md +16 -42
  5. data/app/components/mensa/add_filter/component.html.erb +31 -0
  6. data/app/components/mensa/cell/component.html.erb +1 -0
  7. data/app/components/mensa/column_customizer/component.html.erb +31 -0
  8. data/app/components/mensa/control_bar/component.html.erb +56 -0
  9. data/app/components/mensa/empty_state/component.html.erb +10 -0
  10. data/app/components/mensa/filter_pill/component.html.erb +20 -0
  11. data/app/components/mensa/filter_pill_list/component.html.erb +30 -0
  12. data/app/components/mensa/header/component.html.erb +12 -0
  13. data/app/components/mensa/row_action/component.html.erb +9 -0
  14. data/app/components/mensa/search/component.html.erb +26 -0
  15. data/app/components/mensa/table/component.html.erb +25 -0
  16. data/app/components/mensa/table_row/component.html.erb +18 -0
  17. data/app/components/mensa/view/component.html.erb +51 -0
  18. data/app/components/mensa/views/component.html.erb +74 -0
  19. data/app/views/mensa/exports/_badge.html.erb +6 -0
  20. data/app/views/mensa/exports/_dialog.html.erb +52 -0
  21. data/app/views/mensa/exports/_list.html.erb +32 -0
  22. data/app/views/mensa/tables/filters/show.turbo_stream.erb +58 -0
  23. data/app/views/mensa/tables/show.html.erb +5 -0
  24. data/app/views/mensa/tables/show.turbo_stream.erb +7 -0
  25. data/app/views/mensa/tables/views/create.turbo_stream.erb +11 -0
  26. data/app/views/mensa/tables/views/destroy.turbo_stream.erb +11 -0
  27. data/app/views/mensa/tables/views/update.turbo_stream.erb +11 -0
  28. data/lib/generators/mensa/table_generator.rb +15 -0
  29. data/lib/generators/mensa/templates/config/initializers/mensa.rb +4 -5
  30. data/lib/generators/mensa/templates/table.rb.tt +18 -0
  31. data/lib/mensa/engine.rb +0 -1
  32. data/lib/mensa/version.rb +1 -1
  33. data/mensa.gemspec +2 -4
  34. metadata +29 -54
  35. data/app/components/mensa/add_filter/component.html.slim +0 -14
  36. data/app/components/mensa/cell/component.html.slim +0 -1
  37. data/app/components/mensa/column_customizer/component.html.slim +0 -14
  38. data/app/components/mensa/control_bar/component.html.slim +0 -43
  39. data/app/components/mensa/empty_state/component.html.slim +0 -7
  40. data/app/components/mensa/filter_pill/component.html.slim +0 -9
  41. data/app/components/mensa/filter_pill_list/component.html.slim +0 -11
  42. data/app/components/mensa/header/component.html.slim +0 -8
  43. data/app/components/mensa/row_action/component.html.slim +0 -6
  44. data/app/components/mensa/search/component.html.slim +0 -21
  45. data/app/components/mensa/table/component.html.slim +0 -10
  46. data/app/components/mensa/table_row/component.html.slim +0 -11
  47. data/app/components/mensa/view/component.html.slim +0 -30
  48. data/app/components/mensa/views/component.html.slim +0 -52
  49. data/app/views/mensa/exports/_badge.html.slim +0 -5
  50. data/app/views/mensa/exports/_dialog.html.slim +0 -42
  51. data/app/views/mensa/exports/_list.html.slim +0 -29
  52. data/app/views/mensa/tables/filters/show.turbo_stream.slim +0 -36
  53. data/app/views/mensa/tables/show.html.slim +0 -4
  54. data/app/views/mensa/tables/show.turbo_stream.slim +0 -5
  55. data/app/views/mensa/tables/views/create.turbo_stream.slim +0 -11
  56. data/app/views/mensa/tables/views/destroy.turbo_stream.slim +0 -11
  57. data/app/views/mensa/tables/views/update.turbo_stream.slim +0 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 56a69b51d2f62c342f16b42fc0c0d77fc69101e493486eafea8c89cc358bfd9a
4
- data.tar.gz: 072b02f3d2ebff3ebcd310643b7be1ccb199024771bdb346fd92be4faa9b6ab0
3
+ metadata.gz: 4370665a3982e3464306fd155b455aef8c62d2fda14c474771042a30af3038cb
4
+ data.tar.gz: 91c50234915ffeb886a6569575d33fa7b85eb453f1da8991e075ad46fe5101ac
5
5
  SHA512:
6
- metadata.gz: 4a32e5e1ed6b6a195d8ad4913d6497082e7ac72721bd18410b3135ad502eeef74ae7cf0e91bee2dcc0fae41f3be85018e181bad3f16382d5d2b6bf8698fda70a
7
- data.tar.gz: a6e26eeecd788492da78499a985fc107697bc7916d6562708aa32b3c58a1c681ed2478ee91070db5df5372628d5f1c77d27a0b482a1f9076187b8e4e66e5431d
6
+ metadata.gz: 3cbde51c093adcccaad8b2814b6c436a8be160e6fa68bab0cc2463c22560d325017f736577add7ffb2b3e4974e2df1abce81ca3a89e83ea3bb9e77d42d65800a
7
+ data.tar.gz: 9691484b7695d915849eebbc204f8ca22c560d38928c5e0d78285b4eb1f7b140f29aad0498d455549e359f97590e9659f5e1d2a049b4b3a17ffc2c10bc9c5397
data/Gemfile CHANGED
@@ -10,7 +10,7 @@ gem "sprockets-rails"
10
10
  gem "pry"
11
11
  gem "capybara", "~> 3.40"
12
12
  gem "selenium-webdriver", "~> 4.17"
13
- gem "slim", "~> 5.2"
13
+
14
14
  gem "debug"
15
15
  gem "overmind", require: false
16
16
  gem "rack-mini-profiler"
data/Gemfile.lock CHANGED
@@ -1,14 +1,13 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- mensa (0.3.1)
4
+ mensa (0.3.2)
5
5
  csv
6
6
  importmap-rails
7
7
  pagy (>= 43)
8
8
  pg (>= 1.6)
9
9
  rails (>= 7.1)
10
10
  rubyzip (>= 1.2.2)
11
- slim
12
11
  stimulus-rails
13
12
  tailwindcss-rails (~> 3.3)
14
13
  textacular (>= 5)
@@ -307,9 +306,6 @@ GEM
307
306
  rexml (~> 3.2, >= 3.2.5)
308
307
  rubyzip (>= 1.2.2, < 4.0)
309
308
  websocket (~> 1.0)
310
- slim (5.2.1)
311
- temple (~> 0.10.0)
312
- tilt (>= 2.1.0)
313
309
  sprockets (4.2.2)
314
310
  concurrent-ruby (~> 1.0)
315
311
  logger
@@ -318,14 +314,6 @@ GEM
318
314
  actionpack (>= 6.1)
319
315
  activesupport (>= 6.1)
320
316
  sprockets (>= 3.0.0)
321
- sqlite3 (2.9.4-aarch64-linux-gnu)
322
- sqlite3 (2.9.4-aarch64-linux-musl)
323
- sqlite3 (2.9.4-arm-linux-gnu)
324
- sqlite3 (2.9.4-arm-linux-musl)
325
- sqlite3 (2.9.4-arm64-darwin)
326
- sqlite3 (2.9.4-x86_64-darwin)
327
- sqlite3 (2.9.4-x86_64-linux-gnu)
328
- sqlite3 (2.9.4-x86_64-linux-musl)
329
317
  standard (1.54.0)
330
318
  language_server-protocol (~> 3.17.0.2)
331
319
  lint_roller (~> 1.0)
@@ -349,11 +337,9 @@ GEM
349
337
  tailwindcss-ruby (3.4.19-arm64-darwin)
350
338
  tailwindcss-ruby (3.4.19-x86_64-darwin)
351
339
  tailwindcss-ruby (3.4.19-x86_64-linux)
352
- temple (0.10.4)
353
340
  textacular (5.7.0)
354
341
  activerecord (>= 5.0)
355
342
  thor (1.5.0)
356
- tilt (2.7.0)
357
343
  timeout (0.6.1)
358
344
  tsort (0.2.0)
359
345
  turbo-rails (2.0.23)
@@ -404,9 +390,7 @@ DEPENDENCIES
404
390
  ruby-lsp
405
391
  ruby-lsp-rails
406
392
  selenium-webdriver (~> 4.17)
407
- slim (~> 5.2)
408
393
  sprockets-rails
409
- sqlite3 (~> 2.8)
410
394
  standard
411
395
 
412
396
  BUNDLED WITH
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # Mensa
2
2
 
3
3
  Fast and awesome tables, with pagination, sorting, filtering, batch processing, column hiding, column ordering and custom views.
4
+ Due to search, it only works with postgresql at the moment.
4
5
 
5
6
  ![table](./docs/table.png)
6
7
  ![filters](./docs/filters.png)
@@ -52,16 +53,7 @@ class UserTable < ApplicationTable
52
53
  order name: :desc
53
54
 
54
55
  column(:name) do
55
- attribute :name # Optional, we can deduct this from the column name
56
- sortable true
57
- sanitize true
58
- internal false
59
- visible true
60
- filter do
61
- collection -> { }
62
- scope -> { where(name: ...) }
63
- end
64
- operators [:is, :isnt] # Optional, mensa tries to deduce this from the column information
56
+ filter
65
57
  end
66
58
 
67
59
  column(:nr_of_roles) do
@@ -112,17 +104,14 @@ end
112
104
 
113
105
  You can show your tables on the page using the following:
114
106
 
115
- ```slim
116
- = table :users
107
+ ```erb
108
+ <%= table :users %>
117
109
  ```
118
110
 
119
111
  #### Custom views
120
112
 
121
113
  Custom views are views not defined by the developer (SystemViews) but by the end-user by adding/removing filters.
122
-
123
- Initial support for custom-views is there, but pretty rudimentary:
124
-
125
- `Mensa::TableView.create(table: "users", name: "Guests", config: {filters: {role: {value: "guest"}}})`
114
+ When you enable these, they are stored in the database and can be used across sessions, by the user who created them.
126
115
 
127
116
  ### Fast
128
117
 
@@ -140,42 +129,27 @@ end
140
129
  ### Coding
141
130
 
142
131
  - Checkout this repo
143
- - Setup your direnv, add the following to your `.envrc`:
132
+ - Setup your direnv, add the following to your `mise.toml`:
144
133
 
145
134
  ```
146
- export BUNDLE_RUBYGEMS__PKG__GITHUB__COM=ghp_xxxxxxw
147
- export RBENV_VERSION=$(cat .ruby-version)
135
+ [tools]
136
+ node = "24"
137
+ ruby = "3.4.7"
138
+
139
+ [env]
140
+ RUBY_VERSION="3.4.7"
148
141
  ```
149
142
 
150
143
  - Run `direnv allow`
151
-
152
- If you use devcontainers:
153
- - Open with Visual Studio Code (or with any other editor) and reopen in container.
154
- - Run `bin/overmind s`
155
-
156
- If you're not using devcontainers:
157
- - Run `bin/overmind s`
144
+ - Run `overmind s`
158
145
 
159
146
  ### Docs
160
147
 
161
148
  Using the following in your view will render Mensa::Table::Component
162
- ```slim
163
- = table :users
149
+ ```erb
150
+ <%= table :users %>
164
151
  ```
165
152
 
166
- The Mensa::Table::Component will render:
167
- - Mensa::Search::Component
168
- - Mensa::FilterPillList::Component
169
- - Mensa::FilterPill::Component
170
- - Mensa::AddFilter::Component
171
- - Mensa::Views::Component
172
- - renders a views list
173
- - Mensa::ControlBar::Component
174
- - search icon
175
- - filter icon
176
- - export icon
177
- - turbo-frame with the actual table you see (which is rendered by Mensa::View::Component)
178
-
179
153
  ## Installation
180
154
 
181
155
  Add this line to your application's Gemfile:
@@ -199,7 +173,7 @@ $ gem install mensa
199
173
  Always use `bundle` to install the gem. Next use the install generator to install migrations, add an initializer and do other setup:
200
174
 
201
175
  ```bash
202
- $ bin/rails g mensa:install
176
+ $ rails g mensa:install
203
177
  ```
204
178
 
205
179
  ### Exports
@@ -0,0 +1,31 @@
1
+ <div class="mensa-table__add_filter"
2
+ data-controller="mensa-add-filter"
3
+ data-mensa-add-filter-mensa-filter-pill-list-outlet="#mensa-filter-pill-list-<%= table.table_id %>"
4
+ data-mensa-add-filter-operator-labels-value="<%= operator_labels.to_json %>">
5
+ <button class="mensa-table__add_filter__trigger"
6
+ type="button"
7
+ title="<%= t("mensa.add_filter.add", default: "Add filter") %>"
8
+ data-action="mensa-add-filter#openAllColumns">
9
+ <i class="fa-solid fa-circle-plus"></i>
10
+ </button>
11
+
12
+ <ul class="hidden fixed z-50 max-h-96 overflow-auto rounded-lg bg-white p-2 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none dark:bg-gray-800 sm:text-sm"
13
+ data-mensa-add-filter-target="filterList">
14
+ <% table.columns.select(&:filter?).each do |column| %>
15
+ <li class="text-gray-900 hover:bg-gray-100 relative flex cursor-default select-none items-center gap-2 rounded-md px-3 py-2 dark:text-gray-300 dark:hover:bg-gray-700"
16
+ data-mensa-add-filter-target="filterListItem"
17
+ data-action="click->mensa-add-filter#selectColumn mouseenter->mensa-add-filter#columnItemHovered"
18
+ data-filter-column-name="<%= column.name %>">
19
+ <div class="label block flex-1 truncate font-normal"><%= column.human_name %></div>
20
+ <div class="check hidden flex items-center text-primary-600">
21
+ <div class="fal fa-check"></div>
22
+ </div>
23
+ <span class="mensa-table__add_filter__enter-hint">↵ Enter</span>
24
+ </li>
25
+ <% end %>
26
+ </ul>
27
+
28
+ <div class="hidden mensa-table__add_filter__popover_container"
29
+ data-mensa-add-filter-target="valuePopover"
30
+ id="mensa-filter-pill-value-<%= SecureRandom.base36 %>"></div>
31
+ </div>
@@ -0,0 +1 @@
1
+ <td><%= cell.render(:html) %></td>
@@ -0,0 +1,31 @@
1
+ <div class="relative"
2
+ data-controller="mensa-column-customizer"
3
+ data-mensa-column-customizer-table-name-value="<%= table.name %>"
4
+ data-mensa-column-customizer-turbo-frame-id-value="<%= table.table_id %>"
5
+ data-mensa-column-customizer-mensa-table-outlet="#table-<%= table.table_id %>">
6
+ <button class="mensa-table__control_bar__button" type="button" data-action="click->mensa-column-customizer#toggle">
7
+ <i class="<%= Mensa.config.icons[:control_bar_edit] %>"></i>
8
+ </button>
9
+ <div class="mensa-table__column_customizer__popover hidden" data-mensa-column-customizer-target="popover">
10
+ <p class="mensa-table__column_customizer__heading">Columns</p>
11
+ <ul>
12
+ <% table.columns.reject(&:internal?).each do |column| %>
13
+ <li class="mensa-table__column_customizer__row"
14
+ data-mensa-column-customizer-target="columnRow"
15
+ data-column-name="<%= column.name %>"
16
+ data-visible="<%= column.visible?.to_s %>"
17
+ draggable="true"
18
+ data-action="dragstart->mensa-column-customizer#dragStart dragover->mensa-column-customizer#dragOver drop->mensa-column-customizer#drop dragend->mensa-column-customizer#dragEnd">
19
+ <i class="mensa-table__column_customizer__handle fa-solid fa-grip-vertical"></i>
20
+ <span class="mensa-table__column_customizer__name<%= !column.visible? ? " mensa-table__column_customizer__name--hidden" : "" %>">
21
+ <%= column.human_name %>
22
+ </span>
23
+ <button class="mensa-table__column_customizer__visibility" type="button" data-action="click->mensa-column-customizer#toggleVisibility">
24
+ <i class="fa-solid fa-eye"></i>
25
+ <i class="fa-solid fa-eye-slash"></i>
26
+ </button>
27
+ </li>
28
+ <% end %>
29
+ </ul>
30
+ </div>
31
+ </div>
@@ -0,0 +1,56 @@
1
+ <div class="mensa-table__control_bar" data-mensa-table-target="controlBar">
2
+ <div class="flex items-center gap-1 py-0">
3
+ <% if view_columns_ordering? %>
4
+ <%= render Mensa::ColumnCustomizer::Component.new(table: table) %>
5
+ <% end %>
6
+
7
+ <div class="mensa-table__save-reset hidden" data-mensa-table-target="saveResetButtons">
8
+ <button class="mensa-table__control_bar__button" type="button" title="<%= t(".reset", default: "Reset") %>" data-action="mensa-table#cancelFiltersAndSearch">
9
+ <i class="fa-solid fa-rotate-left"></i>
10
+ </button>
11
+ <% if table.current_user && table.supports_custom_views? %>
12
+ <div class="relative">
13
+ <button class="mensa-table__control_bar__button hidden" type="button" data-mensa-table-target="saveSimple" data-action="mensa-table#saveAsNewView">
14
+ <%= t(".save", default: "Save") %>
15
+ </button>
16
+ <button class="mensa-table__control_bar__button hidden" type="button" data-mensa-table-target="saveSplit" data-action="mensa-table#toggleSaveDropdown">
17
+ <%= t(".save", default: "Save") %>
18
+ <i class="fa-solid fa-chevron-down text-xs"></i>
19
+ </button>
20
+ <ul class="mensa-table__control_bar__save-dropdown hidden" data-mensa-table-target="saveDropdown">
21
+ <li>
22
+ <button class="mensa-table__control_bar__save-dropdown-item" type="button" data-action="mensa-table#updateCurrentViewAction">
23
+ <%= t(".update_view", default: "Update view") %>
24
+ </button>
25
+ </li>
26
+ <li>
27
+ <button class="mensa-table__control_bar__save-dropdown-item" type="button" data-action="mensa-table#saveAsNewView">
28
+ <%= t(".save_as_new_view", default: "Save as new view") %>
29
+ </button>
30
+ </li>
31
+ </ul>
32
+ </div>
33
+ <% else %>
34
+ <button class="mensa-table__control_bar__button" type="button" data-action="mensa-table#saveAsNewView">
35
+ <%= t(".save", default: "Save") %>
36
+ </button>
37
+ <% end %>
38
+ </div>
39
+
40
+ <button class="mensa-table__control_bar__button hidden" type="button" data-mensa-table-target="eyeButton" data-action="click->mensa-table#toggleViewFilters">
41
+ <i class="fa-solid fa-eye"></i>
42
+ </button>
43
+
44
+ <% if table.exportable? %>
45
+ <button class="mensa-table__control_bar__button relative" type="button" data-action="mensa-table#export">
46
+ <i data-mensa-table-target="exportIcon" class="<%= Mensa.config.icons[:control_bar_export] %>"></i>
47
+ <%= helpers.render partial: "mensa/exports/badge", locals: {table_name: table.name, user: table.current_user} %>
48
+ </button>
49
+ <% end %>
50
+ </div>
51
+
52
+ <% if table.exportable? %>
53
+ <%= helpers.turbo_stream_from Mensa::Export.stream_name(table.name, table.current_user) %>
54
+ <%= helpers.render partial: "mensa/exports/dialog", locals: {table: table} %>
55
+ <% end %>
56
+ </div>
@@ -0,0 +1,10 @@
1
+ <div class="mensa-empty-state">
2
+ <div class="mensa-empty-state__icon">
3
+ <i class="<%= Mensa.config.icons[:search] %>"></i>
4
+ </div>
5
+ <h3 class="mensa-empty-state__title"><%= t("mensa.empty_state.title", model: model_name_plural) %></h3>
6
+ <p class="mensa-empty-state__subtitle"><%= t("mensa.empty_state.subtitle") %></p>
7
+ <button class="mensa-empty-state__button" type="button" data-action="click->mensa-table#cancelFiltersAndSearch">
8
+ <%= t("mensa.empty_state.clear_button") %>
9
+ </button>
10
+ </div>
@@ -0,0 +1,20 @@
1
+ <% formatted_value = filter.value.is_a?(Array) ? filter.value.join(", ") : filter.value %>
2
+ <div class="mensa-filter-pill"
3
+ data-controller="mensa-filter-pill"
4
+ data-mensa-filter-pill-mensa-filter-pill-list-outlet="#mensa-filter-pill-list-<%= filter.table.table_id %>"
5
+ data-mensa-filter-pill-column-name-value="<%= filter.column.name %>"
6
+ data-mensa-filter-pill-value-value="<%= filter.operator_with_value? ? (filter.value.is_a?(Array) ? filter.value.to_json : filter.value) : nil %>"
7
+ data-mensa-filter-pill-operator-value="<%= filter.operator %>"
8
+ data-mensa-filter-pill-operator-without-value-value="<%= "true" unless filter.operator_with_value? %>"
9
+ data-view-filter="<%= "true" if view_filter? %>">
10
+ <button class="mensa-filter-pill__chip" type="button" data-action="click->mensa-filter-pill#edit">
11
+ <span class="mensa-filter-pill__column"><%= filter.column.human_name %></span>
12
+ <span class="mensa-filter-pill__operator"><%= filter.operator_label %></span>
13
+ <% if formatted_value.present? && filter.operator_with_value? %>
14
+ <span class="mensa-filter-pill__value"><%= formatted_value %></span>
15
+ <% end %>
16
+ </button>
17
+ <button class="mensa-filter-pill__remove" type="button" title="Remove filter" data-action="click->mensa-filter-pill#remove">
18
+ <i class="fa-solid fa-xmark"></i>
19
+ </button>
20
+ </div>
@@ -0,0 +1,30 @@
1
+ <% has_filterable_columns = table.columns.any?(&:filter?) %>
2
+ <div class="mensa-table__search-bar"
3
+ id="mensa-filter-pill-list-<%= table.table_id %>"
4
+ data-controller="mensa-filter-pill-list"
5
+ data-mensa-table-target="filterList"
6
+ data-mensa-filter-pill-list-table-name-value="<%= table.name %>"
7
+ data-mensa-filter-pill-list-mensa-table-outlet=".mensa-table#table-<%= table.table_id %>"
8
+ data-mensa-filter-pill-list-mensa-filter-pill-outlet=".mensa-filter-pill"
9
+ data-mensa-filter-pill-list-mensa-add-filter-outlet="[data-controller=mensa-add-filter]">
10
+ <div class="mensa-table__search-bar__pills-area">
11
+ <%= render Mensa::FilterPill::Component.with_collection(table.active_filters) %>
12
+ <% if has_filterable_columns %>
13
+ <%= render Mensa::AddFilter::Component.new(table: table) %>
14
+ <% end %>
15
+ <div class="mensa-table__search-bar__input-wrapper">
16
+ <input class="mensa-table__search-bar__input"
17
+ type="text"
18
+ name="search_query"
19
+ autocomplete="off"
20
+ placeholder="<%= t(has_filterable_columns ? ".search" : ".search_only") %>"
21
+ data-mensa-filter-pill-list-target="searchInput"
22
+ data-action="input->mensa-filter-pill-list#monitorSearch keydown.enter->mensa-filter-pill-list#search keydown.esc->mensa-filter-pill-list#resetSearch focus->mensa-filter-pill-list#searchFocused keydown.down->mensa-filter-pill-list#navigateDown keydown.up->mensa-filter-pill-list#navigateUp"
23
+ value="<%= params[:query] %>">
24
+ <i class="mensa-table__search-bar__search-icon <%= Mensa.config.icons[:search] %>"></i>
25
+ </div>
26
+ </div>
27
+ <button class="mensa-table__search-bar__clear hidden" type="button" data-mensa-filter-pill-list-target="resetSearchButton" data-action="mensa-filter-pill-list#resetSearch">
28
+ <i class="fas fa-xmark"></i>
29
+ </button>
30
+ </div>
@@ -0,0 +1,12 @@
1
+ <th>
2
+ <% if column.sortable? %>
3
+ <%= link_to table.path(order: {column.name => column.next_sort_direction}, turbo_frame_id: table.table_id), "data-turbo-frame": "_self", class: "order cursor-pointer" do %>
4
+ <span><%= column.human_name %></span>
5
+ <i class="<%= Mensa.config.icons["order_indicator#{column.sort_direction.to_s.present? ? "_#{column.sort_direction}" : ""}".to_sym] %>"></i>
6
+ <% end %>
7
+ <% else %>
8
+ <div class="container">
9
+ <div class="title"><%= column.human_name %></div>
10
+ </div>
11
+ <% end %>
12
+ </th>
@@ -0,0 +1,9 @@
1
+ <% if action.show.call(row.record) %>
2
+ <%= link_to(action.link ? table.original_view_context.instance_exec(row.record, &action.link) : "", {class: "action", title: action.title}.merge(action.link_attributes || {})) do %>
3
+ <% if action.icon %>
4
+ <i class="fa <%= action.icon %>"></i>
5
+ <% else %>
6
+ <%= name %>
7
+ <% end %>
8
+ <% end %>
9
+ <% end %>
@@ -0,0 +1,26 @@
1
+ <% if table.current_user %>
2
+ <dialog class="mensa-table__save-view-dialog" data-mensa-table-target="saveViewDialog" data-action="click->mensa-table#saveViewDialogBackdrop">
3
+ <form class="mensa-table__save-view-dialog__form" data-action="submit->mensa-table#confirmSaveView">
4
+ <header class="mensa-table__save-view-dialog__header">
5
+ <h2 class="mensa-table__save-view-dialog__title"><%= t(".save_view_title", default: "Save view") %></h2>
6
+ <p class="mensa-table__save-view-dialog__subtitle"><%= t(".save_view_subtitle", default: "Save the current filters, ordering and search as a reusable view.") %></p>
7
+ </header>
8
+ <label class="mensa-table__save-view-dialog__field">
9
+ <span class="mensa-table__save-view-dialog__label"><%= t(".view_name", default: "Name") %></span>
10
+ <input class="mensa-table__save-view-dialog__input" type="text" required placeholder="<%= t(".view_name_placeholder", default: "e.g. Active customers") %>" data-mensa-table-target="saveViewName">
11
+ </label>
12
+ <label class="mensa-table__save-view-dialog__field">
13
+ <span class="mensa-table__save-view-dialog__label"><%= t(".view_description", default: "Description") %></span>
14
+ <textarea class="mensa-table__save-view-dialog__textarea" rows="3" placeholder="<%= t(".view_description_placeholder", default: "Optional notes about this view") %>" data-mensa-table-target="saveViewDescription"></textarea>
15
+ </label>
16
+ <div class="mensa-table__save-view-dialog__actions">
17
+ <button class="mensa-table__save-view-dialog__button mensa-table__save-view-dialog__button--secondary" type="button" data-action="mensa-table#cancelSaveView">
18
+ <%= t(".cancel") %>
19
+ </button>
20
+ <button class="mensa-table__save-view-dialog__button mensa-table__save-view-dialog__button--primary" type="submit">
21
+ <%= t(".save") %>
22
+ </button>
23
+ </div>
24
+ </form>
25
+ </dialog>
26
+ <% end %>
@@ -0,0 +1,25 @@
1
+ <div class="mensa-table"
2
+ id="table-<%= table.table_id %>"
3
+ data-mensa-table-supports-views-value="<%= table.supports_views? %>"
4
+ data-mensa-table-table-url-value="<%= helpers.mensa.table_url(table.name, {turbo_frame_id: table.table_id}.merge(params)) %>"
5
+ data-mensa-table-save-view-url-value="<%= helpers.mensa.table_views_path(table.name) %>"
6
+ data-mensa-table-views-url-value="<%= helpers.mensa.table_views_path(table.name) %>"
7
+ data-mensa-table-exports-url-value="<%= helpers.mensa.table_exports_path(table.name) %>"
8
+ data-controller="mensa-table"
9
+ data-mensa-table-mensa-filter-pill-outlet="[data-controller='mensa-filter-pill']"
10
+ data-mensa-table-mensa-filter-pill-list-outlet="#mensa-filter-pill-list-<%= table.table_id %>"
11
+ data-mensa-table-mensa-column-customizer-outlet="[data-controller='mensa-column-customizer']">
12
+ <div class="mensa-table__toolbar">
13
+ <div class="mensa-table__search-container">
14
+ <% if table.supports_views? && table.show_header? %>
15
+ <%= render Mensa::Views::Component.new(table: table) %>
16
+ <% end %>
17
+ <div class="flex min-w-0 flex-1" id="filters-<%= table.table_id %>">
18
+ <%= render Mensa::FilterPillList::Component.new(table: table) %>
19
+ </div>
20
+ </div>
21
+ <%= render Mensa::ControlBar::Component.new(table: table) %>
22
+ </div>
23
+ <%= render Mensa::Search::Component.new(table: table) %>
24
+ <turbo-frame id="<%= table.table_id %>" target="_top" data-mensa-table-target="turboFrame" data-turbo-permanent=""></turbo-frame>
25
+ </div>
@@ -0,0 +1,18 @@
1
+ <%= content_tag :tr, **row.link_attributes do %>
2
+ <% if table.batch_actions? %>
3
+ <td class="mensa-table__checkbox-col" data-action="click->mensa-selection#stopPropagation">
4
+ <input class="mensa-table__select-all" type="checkbox" value="<%= row.record.id %>" data-action="change->mensa-selection#toggleRow" data-mensa-selection-target="rowCheckbox">
5
+ </td>
6
+ <% end %>
7
+ <% if table.actions? && Mensa.config.row_actions_position == :front %>
8
+ <td class="actions">
9
+ <%= render(Mensa::RowAction::Component.with_collection(table.actions, row: row, table: table)) %>
10
+ </td>
11
+ <% end %>
12
+ <%= render(Mensa::Cell::Component.with_collection(table.display_columns, row: row)) %>
13
+ <% if table.actions? && Mensa.config.row_actions_position == :back %>
14
+ <td class="actions">
15
+ <%= render(Mensa::RowAction::Component.with_collection(table.actions, row: row, table: table)) %>
16
+ </td>
17
+ <% end %>
18
+ <% end %>
@@ -0,0 +1,51 @@
1
+ <div class="overflow-y-auto relative"
2
+ data-mensa-table-target="view"
3
+ data-controller="mensa-selection"
4
+ data-mensa-selection-batch-url-value="<%= table.batch_actions? ? helpers.mensa.table_batch_actions_path(table.name) : "" %>">
5
+ <% if table.batch_actions? %>
6
+ <div class="mensa-batch-bar hidden" data-mensa-selection-target="batchBar">
7
+ <div class="mensa-batch-bar__content">
8
+ <input class="mensa-table__select-all" type="checkbox" data-action="click->mensa-selection#deselectAll" data-mensa-selection-target="batchAllCheckbox">
9
+ <span class="mensa-batch-bar__count" data-mensa-selection-target="selectedCount"></span>
10
+ <% table.batch_actions.each do |batch_action| %>
11
+ <button class="mensa-batch-bar__button" type="button" data-action="click->mensa-selection#executeBatch" data-mensa-selection-batch-action-param="<%= batch_action.name %>">
12
+ <%= batch_action.title %>
13
+ </button>
14
+ <% end %>
15
+ </div>
16
+ </div>
17
+ <% end %>
18
+ <table class="w-full">
19
+ <% if table.show_header? %>
20
+ <thead>
21
+ <tr>
22
+ <% if table.batch_actions? %>
23
+ <th class="mensa-table__checkbox-col">
24
+ <div class="container">
25
+ <input class="mensa-table__select-all" type="checkbox" data-action="change->mensa-selection#toggleAll" data-mensa-selection-target="headerCheckbox">
26
+ </div>
27
+ </th>
28
+ <% end %>
29
+ <% if table.actions? && Mensa.config.row_actions_position == :front %>
30
+ <th>Actions</th>
31
+ <% end %>
32
+ <%= render(Mensa::Header::Component.with_collection(table.display_columns, table: table)) %>
33
+ <% if table.actions? && Mensa.config.row_actions_position == :back %>
34
+ <th>Actions</th>
35
+ <% end %>
36
+ </tr>
37
+ </thead>
38
+ <% end %>
39
+ <tbody>
40
+ <%= render(Mensa::TableRow::Component.with_collection(table.rows, table: table)) %>
41
+ </tbody>
42
+ </table>
43
+ <% if table.pagy_details&.count == 0 %>
44
+ <%= render Mensa::EmptyState::Component.new(table: table) %>
45
+ <% elsif table.pagy_details&.last > 1 %>
46
+ <div class="paging">
47
+ <span><%= t("mensa.paging.info", model: model_name_plural, from: table.pagy_details.from, to: table.pagy_details.to, count: table.pagy_details.count) %></span>
48
+ <%== table.pagy_details.series_nav(anchor_string: 'data-turbo-frame="_self"') %>
49
+ </div>
50
+ <% end %>
51
+ </div>
@@ -0,0 +1,74 @@
1
+ <% current_view = table.all_views.find { |v| v.id == table.table_view&.id } || table.all_views.first %>
2
+ <% current_view_name = current_view&.name || t(".all", default: "All") %>
3
+ <% current_view_id = current_view&.id || "" %>
4
+
5
+ <div class="mensa-table__views"
6
+ id="mensa-views-<%= table.table_id %>"
7
+ data-mensa-table-target="views"
8
+ data-controller="mensa-views"
9
+ data-mensa-views-mensa-filter-pill-list-outlet="#mensa-filter-pill-list-<%= table.table_id %>"
10
+ data-mensa-views-table-id-value="<%= table.table_id %>"
11
+ data-mensa-views-views-url-value="<%= helpers.mensa.table_views_path(table.name) %>">
12
+ <button class="mensa-table__views__trigger" type="button" data-action="mensa-views#toggleDropdown" data-mensa-views-target="trigger" aria-haspopup="listbox">
13
+ <span class="mensa-table__views__trigger-label" data-mensa-views-target="triggerLabel"><%= current_view_name %></span>
14
+ <i class="fa-solid fa-sort mensa-table__views__trigger-icon"></i>
15
+ </button>
16
+
17
+ <div class="mensa-table__views__dropdown hidden" data-mensa-views-target="dropdown">
18
+ <ul role="listbox">
19
+ <% table.all_views.each do |view| %>
20
+ <% is_selected = view.id == table.table_view&.id || (view.id == :default && table.table_view.blank?) %>
21
+ <% is_user_view = view.id.is_a?(String) && view.id.match?(/\A[0-9a-f-]{32,}\z/i) %>
22
+ <li class="mensa-table__views__option" role="option" data-view-id="<%= view.id %>" data-view-name="<%= view.name %>" data-mensa-views-target="view">
23
+ <button class="mensa-table__views__option-btn" type="button" data-action="mensa-views#select" data-view-id="<%= view.id %>">
24
+ <i class="mensa-table__views__option-check fa-solid fa-check<%= " invisible" unless is_selected %>"></i>
25
+ <span><%= view.name %></span>
26
+ </button>
27
+ <% if is_user_view %>
28
+ <button class="mensa-table__views__option-menu" type="button" data-action="mensa-views#toggleSubmenu" data-view-id="<%= view.id %>" title="View options">
29
+ <i class="fa-solid fa-ellipsis"></i>
30
+ </button>
31
+ <% else %>
32
+ <div class="mensa-table__views__option-system">
33
+ <i class="fa-solid fa-ban"></i>
34
+ </div>
35
+ <% end %>
36
+ </li>
37
+ <% end %>
38
+ </ul>
39
+
40
+ <div class="mensa-table__views__submenu hidden" data-mensa-views-target="submenu">
41
+ <button class="mensa-table__views__submenu-item" type="button" data-action="mensa-views#renameView">
42
+ <i class="fa-solid fa-pencil"></i>
43
+ <%= t(".rename_view", default: "Rename view") %>
44
+ </button>
45
+ <button class="mensa-table__views__submenu-item" type="button" data-action="mensa-views#duplicateView">
46
+ <i class="fa-solid fa-copy"></i>
47
+ <%= t(".duplicate_view", default: "Duplicate view") %>
48
+ </button>
49
+ <button class="mensa-table__views__submenu-item mensa-table__views__submenu-item--danger" type="button" data-action="mensa-views#deleteView">
50
+ <i class="fa-solid fa-trash"></i>
51
+ <%= t(".delete_view", default: "Delete view") %>
52
+ </button>
53
+ </div>
54
+ </div>
55
+
56
+ <dialog class="mensa-table__views__rename-dialog" data-mensa-views-target="renameDialog" data-action="click->mensa-views#renameDialogBackdrop">
57
+ <form class="mensa-table__views__rename-form" data-action="submit->mensa-views#confirmRename">
58
+ <input type="hidden" data-mensa-views-target="renameViewId">
59
+ <h3 class="mensa-table__views__rename-title"><%= t(".rename_view", default: "Rename view") %></h3>
60
+ <label class="mensa-table__views__rename-label">
61
+ <%= t(".view_name", default: "Name") %>
62
+ <input class="mensa-table__views__rename-input" type="text" required placeholder="<%= t(".view_name_placeholder", default: "View name") %>" data-mensa-views-target="renameInput">
63
+ </label>
64
+ <div class="mensa-table__views__rename-actions">
65
+ <button class="mensa-table__views__rename-btn mensa-table__views__rename-btn--secondary" type="button" data-action="mensa-views#cancelRename">
66
+ <%= t(".cancel", default: "Cancel") %>
67
+ </button>
68
+ <button class="mensa-table__views__rename-btn mensa-table__views__rename-btn--primary" type="submit">
69
+ <%= t(".rename", default: "Rename") %>
70
+ </button>
71
+ </div>
72
+ </form>
73
+ </dialog>
74
+ </div>
@@ -0,0 +1,6 @@
1
+ <% count = Mensa::Export.completed_count(table_name, user) %>
2
+ <span id="<%= Mensa::Export.badge_dom_id(table_name, user) %>"
3
+ class="mensa-table__control_bar__badge <%= 'hidden' unless count.positive? %>"
4
+ aria-hidden="<%= count.positive? ? 'false' : 'true' %>">
5
+ <%= count %>
6
+ </span>