mensa 0.4.0 → 0.6.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.
- checksums.yaml +4 -4
- data/.zed/tasks.json +1 -1
- data/Gemfile +1 -0
- data/Gemfile.lock +12 -8
- data/README.md +30 -15
- data/app/components/mensa/add_filter/component.css +5 -3
- data/app/components/mensa/add_filter/component.html.erb +3 -3
- data/app/components/mensa/add_filter/component_controller.js +1 -0
- data/app/components/mensa/column_customizer/component.css +4 -4
- data/app/components/mensa/column_customizer/component.html.erb +4 -4
- data/app/components/mensa/column_customizer/component_controller.js +1 -1
- data/app/components/mensa/control_bar/component.css +23 -3
- data/app/components/mensa/control_bar/component.html.erb +13 -12
- data/app/components/mensa/empty_state/component.html.erb +1 -1
- data/app/components/mensa/empty_state/component.rb +1 -1
- data/app/components/mensa/filter_pill/component.html.erb +2 -2
- data/app/components/mensa/filter_pill_list/component.html.erb +2 -2
- data/app/components/mensa/header/component.html.erb +1 -1
- data/app/components/mensa/table/component.html.erb +8 -4
- data/app/components/mensa/table/component.rb +3 -3
- data/app/components/mensa/table/component_controller.js +104 -30
- data/app/components/mensa/view/component.html.erb +1 -1
- data/app/components/mensa/view/component.rb +1 -1
- data/app/components/mensa/views/component.html.erb +8 -8
- data/app/controllers/mensa/tables/exports_controller.rb +25 -5
- data/app/controllers/mensa/tables_controller.rb +8 -10
- data/app/helpers/mensa/application_helper.rb +2 -2
- data/app/jobs/mensa/export_job.rb +4 -0
- data/app/jobs/mensa/recurring_exports_job.rb +17 -0
- data/app/models/mensa/export.rb +54 -2
- data/app/tables/mensa/base.rb +55 -13
- data/app/tables/mensa/cell.rb +8 -4
- data/app/tables/mensa/column.rb +5 -0
- data/app/tables/mensa/config/column_dsl.rb +1 -0
- data/app/tables/mensa/config/dsl_logic.rb +19 -1
- data/app/tables/mensa/config/format_dsl.rb +7 -0
- data/app/tables/mensa/config/table_dsl.rb +2 -5
- data/app/tables/mensa/filter.rb +24 -1
- data/app/tables/mensa/format.rb +23 -0
- data/app/tables/mensa/scope.rb +5 -3
- data/app/views/mensa/exports/_dialog.html.erb +25 -2
- data/app/views/mensa/exports/_list.html.erb +15 -2
- data/app/views/mensa/tables/filters/show.turbo_stream.erb +4 -1
- data/app/views/mensa/tables/views/destroy.turbo_stream.erb +5 -1
- data/config/locales/en.yml +25 -0
- data/config/locales/nl.yml +24 -0
- data/config/routes.rb +1 -1
- data/db/migrate/20260612110000_add_repeat_to_mensa_exports.rb +8 -0
- data/lib/generators/mensa/templates/config/initializers/mensa.rb +29 -8
- data/lib/mensa/configuration.rb +64 -17
- data/lib/mensa/version.rb +1 -1
- data/lib/tasks/mensa_tasks.rake +7 -0
- metadata +6 -2
data/app/tables/mensa/base.rb
CHANGED
|
@@ -8,26 +8,24 @@ module Mensa
|
|
|
8
8
|
|
|
9
9
|
attr_writer :original_view_context
|
|
10
10
|
attr_accessor :component, :name, :table_view, :request
|
|
11
|
-
attr_reader :params
|
|
12
11
|
|
|
12
|
+
config_reader :params
|
|
13
13
|
config_reader :model
|
|
14
14
|
config_reader :scope
|
|
15
15
|
config_reader :link, call: false
|
|
16
|
-
config_reader :supports_views?
|
|
17
16
|
config_reader :supports_custom_views?
|
|
18
|
-
config_reader :supports_filters?
|
|
19
17
|
config_reader :view_columns_ordering?
|
|
20
18
|
config_reader :show_header?
|
|
21
19
|
config_reader :exportable?
|
|
22
20
|
config_reader :export_with_password?
|
|
23
21
|
|
|
24
22
|
def initialize(config = {})
|
|
25
|
-
|
|
26
|
-
@config = self.class.definition.merge(
|
|
23
|
+
normalized_config = config.to_h.deep_symbolize_keys
|
|
24
|
+
@config = self.class.definition.merge(normalized_config)
|
|
25
|
+
@params = (@config[:params].presence || {}).deep_symbolize_keys
|
|
26
|
+
@config[:params] = @params
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
params[:hidden_columns]&.each do |column_name|
|
|
28
|
+
current_hidden_columns&.each do |column_name|
|
|
31
29
|
c = columns.find { |c| c.name == column_name.to_sym }
|
|
32
30
|
c.config[:visible] = false
|
|
33
31
|
end
|
|
@@ -48,12 +46,17 @@ module Mensa
|
|
|
48
46
|
|
|
49
47
|
# Returns all columns
|
|
50
48
|
def columns
|
|
49
|
+
ensure_internal_columns_for_joined_associations
|
|
51
50
|
@columns ||= column_order.map { |column_name| Mensa::Column.new(column_name, config: config.dig(:columns, column_name), table: self) }
|
|
52
51
|
end
|
|
53
52
|
|
|
54
53
|
# Returns a column by name
|
|
55
54
|
# @param [String] name
|
|
56
55
|
def column(name)
|
|
56
|
+
found_column = columns.find { |c| c.name == name.to_sym }
|
|
57
|
+
return found_column if found_column || @internal_columns_ensured
|
|
58
|
+
|
|
59
|
+
@columns = nil
|
|
57
60
|
columns.find { |c| c.name == name.to_sym }
|
|
58
61
|
end
|
|
59
62
|
|
|
@@ -117,16 +120,19 @@ module Mensa
|
|
|
117
120
|
end
|
|
118
121
|
|
|
119
122
|
# Returns the current path with configuration
|
|
120
|
-
def path(order: {}, turbo_frame_id:
|
|
123
|
+
def path(order: {}, turbo_frame_id: current_turbo_frame_id, table_view_id: current_table_view_id, column_order: current_column_order, hidden_columns: current_hidden_columns, user_params: nil)
|
|
121
124
|
# FIXME: if someone doesn't use as: :mensa in the routes, it breaks
|
|
122
|
-
original_view_context.mensa.table_path(
|
|
123
|
-
|
|
125
|
+
path = original_view_context.mensa.table_path(name)
|
|
126
|
+
query = {
|
|
127
|
+
params: user_params || params,
|
|
124
128
|
order: order_hash(order),
|
|
125
129
|
turbo_frame_id: turbo_frame_id,
|
|
126
130
|
table_view_id: table_view_id,
|
|
127
131
|
column_order: column_order,
|
|
128
132
|
hidden_columns: hidden_columns
|
|
129
|
-
|
|
133
|
+
}.compact.to_query
|
|
134
|
+
|
|
135
|
+
query.present? ? "#{path}?#{query}" : path
|
|
130
136
|
end
|
|
131
137
|
|
|
132
138
|
def all_views
|
|
@@ -135,6 +141,10 @@ module Mensa
|
|
|
135
141
|
views
|
|
136
142
|
end
|
|
137
143
|
|
|
144
|
+
def views?
|
|
145
|
+
all_views.reject{_1.id == :default}.present?
|
|
146
|
+
end
|
|
147
|
+
|
|
138
148
|
# The user that owns custom views. Returns nil when the host application has
|
|
139
149
|
# no current user, in which case views cannot be saved.
|
|
140
150
|
def current_user
|
|
@@ -146,21 +156,53 @@ module Mensa
|
|
|
146
156
|
def table_id
|
|
147
157
|
return @table_id if @table_id
|
|
148
158
|
|
|
149
|
-
@table_id =
|
|
159
|
+
@table_id = current_turbo_frame_id || "#{name.to_s.gsub("/", "__")}-#{SecureRandom.base36}"
|
|
150
160
|
end
|
|
151
161
|
|
|
152
162
|
def original_view_context
|
|
153
163
|
@original_view_context || component.original_view_context
|
|
154
164
|
end
|
|
155
165
|
|
|
166
|
+
def current_query
|
|
167
|
+
config[:query]
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def current_order
|
|
171
|
+
config[:order]
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def current_order_provided?
|
|
175
|
+
config.key?(:order)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def current_table_view_id
|
|
179
|
+
config[:table_view_id]
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def current_column_order
|
|
183
|
+
config[:column_order]
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def current_hidden_columns
|
|
187
|
+
config[:hidden_columns]
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def current_turbo_frame_id
|
|
191
|
+
config[:turbo_frame_id]
|
|
192
|
+
end
|
|
193
|
+
|
|
156
194
|
private
|
|
157
195
|
|
|
158
196
|
def ensure_internal_columns_for_joined_associations
|
|
197
|
+
return if @internal_columns_ensured
|
|
198
|
+
|
|
159
199
|
config[:columns] ||= {}
|
|
160
200
|
|
|
161
201
|
auto_internal_column_names.each do |column_name|
|
|
162
202
|
config[:columns][column_name] ||= {internal: true, filter: false}
|
|
163
203
|
end
|
|
204
|
+
|
|
205
|
+
@internal_columns_ensured = true
|
|
164
206
|
end
|
|
165
207
|
|
|
166
208
|
def auto_internal_column_names
|
data/app/tables/mensa/cell.rb
CHANGED
|
@@ -38,10 +38,14 @@ module Mensa
|
|
|
38
38
|
content_tag(:i, "", class: "fa-solid fa-check")
|
|
39
39
|
when FalseClass
|
|
40
40
|
content_tag(:i, "", class: "fa-solid fa-xmark")
|
|
41
|
+
when Array
|
|
42
|
+
value.to_fs(:db)
|
|
41
43
|
when Date
|
|
42
|
-
|
|
44
|
+
I18n.l(value.in_time_zone(column.format.time_zone), format: column.format.format)
|
|
45
|
+
# value.in_time_zone(column.format.time_zone).to_fs(column.format.format)
|
|
43
46
|
when Time, DateTime
|
|
44
|
-
|
|
47
|
+
I18n.l(value.in_time_zone(column.format.time_zone), format: column.format.format)
|
|
48
|
+
# value.in_time_zone(column.format.time_zone).to_fs(column.format.format)
|
|
45
49
|
else
|
|
46
50
|
column.sanitize? ? sanitize(value.to_s) : value.to_s.html_safe
|
|
47
51
|
end
|
|
@@ -54,9 +58,9 @@ module Mensa
|
|
|
54
58
|
when TrueClass, FalseClass
|
|
55
59
|
value.to_s
|
|
56
60
|
when Date
|
|
57
|
-
|
|
61
|
+
I18n.l(value.in_time_zone(column.format.time_zone), format: column.format.format)
|
|
58
62
|
when Time, DateTime
|
|
59
|
-
|
|
63
|
+
value.in_time_zone(column.format.time_zone).to_fs(column.format.format)
|
|
60
64
|
else
|
|
61
65
|
strip_tags(value.to_s)
|
|
62
66
|
end
|
data/app/tables/mensa/column.rb
CHANGED
|
@@ -19,6 +19,7 @@ module Mensa
|
|
|
19
19
|
config_reader :visible?
|
|
20
20
|
config_reader :internal?
|
|
21
21
|
config_reader :method # When a method needs to be called on the model, slow!
|
|
22
|
+
config_reader :format
|
|
22
23
|
|
|
23
24
|
def sort_direction
|
|
24
25
|
value = table.config.dig(:order, name)
|
|
@@ -88,6 +89,10 @@ module Mensa
|
|
|
88
89
|
@filter ||= Mensa::Filter.new(column: self, config: table.config.dig(:columns, name, :filter) || {}, table: table)
|
|
89
90
|
end
|
|
90
91
|
|
|
92
|
+
def format
|
|
93
|
+
@format ||= Mensa::Format.new(config: config.dig(:format).presence || {}, column: self)
|
|
94
|
+
end
|
|
95
|
+
|
|
91
96
|
def human_name
|
|
92
97
|
if table.model < ActiveRecord::Base
|
|
93
98
|
table.model.human_attribute_name name
|
|
@@ -16,6 +16,7 @@ module Mensa::Config
|
|
|
16
16
|
option :internal, default: false
|
|
17
17
|
option :method
|
|
18
18
|
option :type
|
|
19
|
+
option :format, default: :long, dsl_single_hash: Mensa::Config::FormatDsl, name_attribute: :format
|
|
19
20
|
|
|
20
21
|
option :visible, default: true
|
|
21
22
|
option :render, dsl: Mensa::Config::RenderDsl
|
|
@@ -17,11 +17,13 @@ module Mensa::Config
|
|
|
17
17
|
class_methods do
|
|
18
18
|
attr_reader :default_config
|
|
19
19
|
|
|
20
|
-
def option(name, default: nil, config_name: nil, dsl: nil, dsl_hash: nil, dsl_array: nil)
|
|
20
|
+
def option(name, default: nil, config_name: nil, name_attribute: nil, dsl: nil, dsl_hash: nil, dsl_array: nil, dsl_single_hash: nil)
|
|
21
21
|
if dsl
|
|
22
22
|
dsl_option(name, dsl)
|
|
23
23
|
elsif dsl_hash
|
|
24
24
|
dsl_hash(name, dsl_hash, config_name: config_name)
|
|
25
|
+
elsif dsl_single_hash
|
|
26
|
+
dsl_single_hash(name, dsl_single_hash, name_attribute:, default: default)
|
|
25
27
|
elsif dsl_array
|
|
26
28
|
dsl_array(name, dsl_array, config_name: config_name)
|
|
27
29
|
else
|
|
@@ -80,6 +82,22 @@ module Mensa::Config
|
|
|
80
82
|
end
|
|
81
83
|
end
|
|
82
84
|
|
|
85
|
+
###
|
|
86
|
+
# Define a DSL that builds a single hash
|
|
87
|
+
# by calling option :action, dsl_single_hash: Mensa::ActionDsl
|
|
88
|
+
#
|
|
89
|
+
def dsl_single_hash(option_name, klass, name_attribute: :name, default: nil)
|
|
90
|
+
config_name ||= option_name.to_s.to_sym
|
|
91
|
+
|
|
92
|
+
@default_config ||= {}
|
|
93
|
+
@default_config[option_name.to_sym] = default.nil? ? {} : {name_attribute => default}
|
|
94
|
+
|
|
95
|
+
define_method(option_name) do |name = nil, &block|
|
|
96
|
+
config[config_name] = {name_attribute => name}
|
|
97
|
+
config[config_name].merge!(klass.new(nil, &block).config.compact)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
83
101
|
###
|
|
84
102
|
# Define a DSL that builds an array
|
|
85
103
|
# by calling option :action, dsl_array: Mensa::ActionDsl
|
|
@@ -30,8 +30,6 @@
|
|
|
30
30
|
# order last_name: :asc
|
|
31
31
|
# link { |user| edit_user_path(user) }
|
|
32
32
|
#
|
|
33
|
-
# supports_views true
|
|
34
|
-
# supports_filters true
|
|
35
33
|
#
|
|
36
34
|
# action :activate do
|
|
37
35
|
# link { |user| edit_user_path(user) }
|
|
@@ -48,6 +46,7 @@ module Mensa::Config
|
|
|
48
46
|
class TableDsl
|
|
49
47
|
include DslLogic
|
|
50
48
|
|
|
49
|
+
option :params, default: {}
|
|
51
50
|
option :model, default: -> {
|
|
52
51
|
begin
|
|
53
52
|
self.class.name.demodulize.to_s.classify.gsub("Table", "").singularize.constantize
|
|
@@ -75,9 +74,7 @@ module Mensa::Config
|
|
|
75
74
|
|
|
76
75
|
option :render, dsl: Mensa::Config::RenderDsl
|
|
77
76
|
|
|
78
|
-
option :
|
|
79
|
-
option :supports_custom_views, default: false
|
|
80
|
-
option :supports_filters, default: true
|
|
77
|
+
option :supports_custom_views, default: true
|
|
81
78
|
option :show_header, default: true
|
|
82
79
|
# Whether the table allows to change column ordering
|
|
83
80
|
option :view_columns_ordering, default: true
|
data/app/tables/mensa/filter.rb
CHANGED
|
@@ -60,11 +60,20 @@ module Mensa
|
|
|
60
60
|
|
|
61
61
|
def to_s
|
|
62
62
|
parts = [column.human_name, operator_label]
|
|
63
|
-
formatted_value = value.is_a?(Array) ? value.join(", ") : value
|
|
64
63
|
parts << formatted_value if formatted_value.present? && operator_with_value?
|
|
65
64
|
parts.join(" ")
|
|
66
65
|
end
|
|
67
66
|
|
|
67
|
+
def formatted_value
|
|
68
|
+
options = collection_options
|
|
69
|
+
|
|
70
|
+
if value.is_a?(Array)
|
|
71
|
+
value.map { |entry| label_for_value(entry, options) }.join(", ")
|
|
72
|
+
else
|
|
73
|
+
label_for_value(value, options)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
68
77
|
def filter_scope(record_scope)
|
|
69
78
|
if scope
|
|
70
79
|
record_scope.instance_exec(normalize(value), &scope)
|
|
@@ -140,6 +149,20 @@ module Mensa
|
|
|
140
149
|
|
|
141
150
|
private
|
|
142
151
|
|
|
152
|
+
def label_for_value(selected_value, options)
|
|
153
|
+
option = options.find { |_label, value| value.to_s == selected_value.to_s }
|
|
154
|
+
option ? option.first : selected_value
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def collection_options
|
|
158
|
+
collection = column.filter&.collection
|
|
159
|
+
return [] if collection.blank?
|
|
160
|
+
|
|
161
|
+
collection.map do |item|
|
|
162
|
+
item.is_a?(Array) ? [item.first.to_s, item.last.to_s] : [item.to_s, item.to_s]
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
143
166
|
# Unused at the moment
|
|
144
167
|
def normalize(query)
|
|
145
168
|
query # .to_s.gsub(/\s(?![&!|])/, '\\\\ ')
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mensa
|
|
4
|
+
# Represents a batch action that can be applied to multiple selected records.
|
|
5
|
+
#
|
|
6
|
+
# batch :archive do
|
|
7
|
+
# title "Archive"
|
|
8
|
+
# process { |records| records.update_all(archived: true) }
|
|
9
|
+
# end
|
|
10
|
+
class Format
|
|
11
|
+
include ConfigReaders
|
|
12
|
+
|
|
13
|
+
defined_by Mensa::Config::FormatDsl
|
|
14
|
+
|
|
15
|
+
config_reader :format
|
|
16
|
+
config_reader :time_zone
|
|
17
|
+
|
|
18
|
+
def initialize(config:, column:)
|
|
19
|
+
@column = column
|
|
20
|
+
@config = self.class.definition.merge(config || {})
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
data/app/tables/mensa/scope.rb
CHANGED
|
@@ -14,7 +14,7 @@ module Mensa
|
|
|
14
14
|
return @filtered_scope if @filtered_scope
|
|
15
15
|
|
|
16
16
|
@filtered_scope = scope
|
|
17
|
-
@filtered_scope = search(@filtered_scope,
|
|
17
|
+
@filtered_scope = search(@filtered_scope, current_query) if current_query.present?
|
|
18
18
|
|
|
19
19
|
# Use inject
|
|
20
20
|
active_filters.each do |filter|
|
|
@@ -44,6 +44,8 @@ module Mensa
|
|
|
44
44
|
def selected_scope
|
|
45
45
|
return @selected_scope if @selected_scope
|
|
46
46
|
|
|
47
|
+
ensure_internal_columns_for_joined_associations
|
|
48
|
+
|
|
47
49
|
@selected_scope = ordered_scope
|
|
48
50
|
@selected_scope = @selected_scope.select([:id] + columns.filter_map(&:attribute))
|
|
49
51
|
|
|
@@ -72,7 +74,7 @@ module Mensa
|
|
|
72
74
|
# (even with blank values), use only those — blank means "explicitly no sort".
|
|
73
75
|
# Falls back to the view/config default only when no order params were sent.
|
|
74
76
|
def effective_order
|
|
75
|
-
result =
|
|
77
|
+
result = current_order_provided? ? (current_order || {}) : (config[:order] || {})
|
|
76
78
|
result = result.symbolize_keys.compact_blank.transform_values(&:to_sym)
|
|
77
79
|
result.transform_keys { |column_name| column(column_name).attribute_for_condition }
|
|
78
80
|
end
|
|
@@ -81,7 +83,7 @@ module Mensa
|
|
|
81
83
|
# nil values become "" so they appear in the URL as order[col]= (which tells
|
|
82
84
|
# the server the user explicitly cleared that column's sort direction).
|
|
83
85
|
def order_hash(new_params = {})
|
|
84
|
-
base =
|
|
86
|
+
base = current_order&.symbolize_keys || config[:order]&.symbolize_keys || {}
|
|
85
87
|
merged = base.merge(new_params.symbolize_keys)
|
|
86
88
|
merged.transform_values { |v| v.nil? ? "" : v.to_sym }
|
|
87
89
|
end
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<header class="mensa-table__export-dialog__header">
|
|
6
6
|
<h2 class="mensa-table__export-dialog__title"><%= t("mensa.exports.title", default: "Export %{table}", table: table.name.to_s.humanize) %></h2>
|
|
7
7
|
<button class="mensa-table__export-dialog__close" type="button" data-action="mensa-table#cancelExport" aria-label="<%= t("mensa.exports.close", default: "Close") %>">
|
|
8
|
-
<i class="
|
|
8
|
+
<i class="<%= Mensa.config.icons[:exports_dialog_close] %>"></i>
|
|
9
9
|
</button>
|
|
10
10
|
</header>
|
|
11
11
|
|
|
@@ -37,12 +37,35 @@
|
|
|
37
37
|
</label>
|
|
38
38
|
</fieldset>
|
|
39
39
|
|
|
40
|
+
<fieldset class="mensa-table__export-dialog__fieldset">
|
|
41
|
+
<legend class="mensa-table__export-dialog__section-title"><%= t("mensa.exports.repeat_section", default: "Repeat") %></legend>
|
|
42
|
+
<label class="mensa-table__export-dialog__option">
|
|
43
|
+
<input type="radio" name="repeat_mode" value="once" checked data-action="change->mensa-table#toggleExportRepeat">
|
|
44
|
+
<span><%= t("mensa.exports.repeat_once", default: "Only once") %></span>
|
|
45
|
+
</label>
|
|
46
|
+
<label class="mensa-table__export-dialog__option">
|
|
47
|
+
<input type="radio" name="repeat_mode" value="repeating" data-action="change->mensa-table#toggleExportRepeat">
|
|
48
|
+
<span><%= t("mensa.exports.repeat_repeating", default: "Repeating") %></span>
|
|
49
|
+
</label>
|
|
50
|
+
|
|
51
|
+
<div class="mensa-table__export-dialog__repeat-options" hidden data-mensa-table-repeat-options>
|
|
52
|
+
<select name="repeat" class="mensa-table__export-dialog__select">
|
|
53
|
+
<option value="daily"><%= t("mensa.exports.repeat_intervals.daily", default: "daily") %></option>
|
|
54
|
+
<option value="weekly"><%= t("mensa.exports.repeat_intervals.weekly", default: "weekly") %></option>
|
|
55
|
+
<option value="monthly"><%= t("mensa.exports.repeat_intervals.monthly", default: "monthly") %></option>
|
|
56
|
+
<option value="quarterly"><%= t("mensa.exports.repeat_intervals.quarterly", default: "quarterly") %></option>
|
|
57
|
+
<option value="bi-yearly"><%= t("mensa.exports.repeat_intervals.bi-yearly", default: "bi-yearly") %></option>
|
|
58
|
+
<option value="yearly"><%= t("mensa.exports.repeat_intervals.yearly", default: "yearly") %></option>
|
|
59
|
+
</select>
|
|
60
|
+
</div>
|
|
61
|
+
</fieldset>
|
|
62
|
+
|
|
40
63
|
<div class="mensa-table__export-dialog__actions">
|
|
41
64
|
<button class="mensa-table__export-dialog__button mensa-table__export-dialog__button--secondary" type="button" data-action="mensa-table#cancelExport">
|
|
42
65
|
<%= t("mensa.exports.cancel", default: "Cancel") %>
|
|
43
66
|
</button>
|
|
44
67
|
<button class="mensa-table__export-dialog__button mensa-table__export-dialog__button--primary" type="submit">
|
|
45
|
-
<i class="
|
|
68
|
+
<i class="<%= Mensa.config.icons[:exports_dialog_submit] %>"></i>
|
|
46
69
|
<%= t("mensa.exports.submit", default: "Export") %>
|
|
47
70
|
</button>
|
|
48
71
|
</div>
|
|
@@ -6,21 +6,34 @@
|
|
|
6
6
|
<ul class="mensa-table__export-dialog__list">
|
|
7
7
|
<% exports.each do |export| %>
|
|
8
8
|
<li class="mensa-table__export-dialog__item">
|
|
9
|
+
<%= button_to mensa.table_export_path(export.table_name, export),
|
|
10
|
+
method: :delete,
|
|
11
|
+
class: "mensa-table__export-dialog__delete",
|
|
12
|
+
form_class: "mensa-table__export-dialog__delete-form",
|
|
13
|
+
aria: {label: t("mensa.exports.delete", default: "Delete export") } do %>
|
|
14
|
+
<i class="<%= Mensa.config.icons[:exports_list_delete] %>"></i>
|
|
15
|
+
<% end %>
|
|
16
|
+
|
|
9
17
|
<div class="mensa-table__export-dialog__item-info">
|
|
10
18
|
<span class="mensa-table__export-dialog__item-name"><%= export.filename.presence || t("mensa.exports.item_name", default: "%{table} export", table: export.table_name.to_s.humanize) %></span>
|
|
19
|
+
<% if export.repeating? %>
|
|
20
|
+
<span class="mensa-table__export-dialog__item-repeat"><%= export.repeat_label %></span>
|
|
21
|
+
<% end %>
|
|
11
22
|
<span class="mensa-table__export-dialog__item-meta"><%= export.created_at.strftime("%Y-%m-%d %H:%M") %></span>
|
|
12
23
|
</div>
|
|
13
24
|
<div class="mensa-table__export-dialog__item-action">
|
|
14
25
|
<% if export.downloadable? %>
|
|
15
26
|
<%= link_to mensa.download_table_export_path(export.table_name, export), class: "mensa-table__export-dialog__download", data: {turbo: false} do %>
|
|
16
|
-
<i class="
|
|
27
|
+
<i class="<%= Mensa.config.icons[:exports_list_download] %>"></i>
|
|
17
28
|
<%= t("mensa.exports.download", default: "Download") %>
|
|
18
29
|
<% end %>
|
|
19
30
|
<% elsif export.failed? %>
|
|
20
31
|
<span class="mensa-table__export-dialog__status mensa-table__export-dialog__status--failed"><%= t("mensa.exports.failed", default: "Failed") %></span>
|
|
32
|
+
<% elsif export.completed? %>
|
|
33
|
+
<span class="mensa-table__export-dialog__status mensa-table__export-dialog__status--pending"><%= t("mensa.exports.waiting", default: "Waiting") %></span>
|
|
21
34
|
<% else %>
|
|
22
35
|
<span class="mensa-table__export-dialog__status mensa-table__export-dialog__status--pending">
|
|
23
|
-
<i class="
|
|
36
|
+
<i class="<%= Mensa.config.icons[:exports_list_processing] %>"></i>
|
|
24
37
|
<%= t("mensa.exports.processing", default: "Preparing…") %>
|
|
25
38
|
</span>
|
|
26
39
|
<% end %>
|
|
@@ -54,5 +54,8 @@
|
|
|
54
54
|
<% end %>
|
|
55
55
|
</ul>
|
|
56
56
|
<hr class="mensa-table__add_filter__popover_container__separator">
|
|
57
|
-
<a class="mensa-table__add_filter__popover_container__clear" href="#" data-action="click->mensa-add-filter#reset">
|
|
57
|
+
<a class="mensa-table__add_filter__popover_container__clear" href="#" data-action="click->mensa-add-filter#reset">
|
|
58
|
+
<span class="flex-1"><%= t('mensa.tables.filters.show.clear') %></span>
|
|
59
|
+
<span class="mensa-table__add_filter__enter-hint">⎋ Esc</span>
|
|
60
|
+
</a>
|
|
58
61
|
<% end %>
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
<%= turbo_stream.replace "mensa-views-#{@table.table_id}" do %>
|
|
2
|
-
|
|
2
|
+
<% if @table.views? %>
|
|
3
|
+
<%= render Mensa::Views::Component.new(table: @table) %>
|
|
4
|
+
<% else %>
|
|
5
|
+
<div id="mensa-views-<%= @table.table_id %>"></div>
|
|
6
|
+
<% end %>
|
|
3
7
|
<% end %>
|
|
4
8
|
|
|
5
9
|
<%= turbo_stream.update "filters-#{@table.table_id}" do %>
|
data/config/locales/en.yml
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
en:
|
|
2
2
|
mensa:
|
|
3
|
+
tables:
|
|
4
|
+
filters:
|
|
5
|
+
show:
|
|
6
|
+
clear: Cancel
|
|
7
|
+
actions: Actions
|
|
8
|
+
save: Save
|
|
9
|
+
update_view: Update view
|
|
10
|
+
save_view: Save as new view
|
|
11
|
+
save_as_new_view: Save as new view
|
|
3
12
|
operators:
|
|
4
13
|
is: is
|
|
5
14
|
isnt: isn't
|
|
@@ -12,6 +21,7 @@ en:
|
|
|
12
21
|
lteq: less than or equal to
|
|
13
22
|
is_empty: is empty
|
|
14
23
|
add_filter:
|
|
24
|
+
add: Add filter
|
|
15
25
|
component:
|
|
16
26
|
add_filter: Add filter
|
|
17
27
|
filter_pill_list:
|
|
@@ -20,6 +30,7 @@ en:
|
|
|
20
30
|
search_only: Search
|
|
21
31
|
search:
|
|
22
32
|
component:
|
|
33
|
+
search: Search and filter
|
|
23
34
|
search_in: Search in %{view}
|
|
24
35
|
cancel: Cancel
|
|
25
36
|
save: Save
|
|
@@ -35,8 +46,22 @@ en:
|
|
|
35
46
|
available_downloads: Available downloads
|
|
36
47
|
empty: You have no downloads yet. Create one below.
|
|
37
48
|
item_name: "%{table} export"
|
|
49
|
+
repeats_with_interval: "Repeats %{interval}"
|
|
50
|
+
repeat_section: Repeat
|
|
51
|
+
repeat: Repeat
|
|
52
|
+
repeat_once: "Only once"
|
|
53
|
+
repeat_repeating: Repeating
|
|
54
|
+
repeat_intervals:
|
|
55
|
+
daily: daily
|
|
56
|
+
weekly: weekly
|
|
57
|
+
monthly: monthly
|
|
58
|
+
quarterly: quarterly
|
|
59
|
+
bi-yearly: bi-yearly
|
|
60
|
+
yearly: yearly
|
|
38
61
|
download: Download
|
|
62
|
+
delete: Delete export
|
|
39
63
|
processing: Preparing…
|
|
64
|
+
waiting: Waiting
|
|
40
65
|
failed: Failed
|
|
41
66
|
new_export: New export
|
|
42
67
|
scope_all: All records (matching current filters)
|
data/config/locales/nl.yml
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
nl:
|
|
2
2
|
mensa:
|
|
3
|
+
tables:
|
|
4
|
+
filters:
|
|
5
|
+
show:
|
|
6
|
+
clear: Annuleren
|
|
7
|
+
actions: Acties
|
|
8
|
+
save: Bewaar
|
|
9
|
+
update_view: Bewaar weergave
|
|
10
|
+
save_view: Bewaar als nieuwe weergave
|
|
11
|
+
save_as_new_view: Bewaar als nieuwe weergave
|
|
3
12
|
operators:
|
|
4
13
|
is: is
|
|
5
14
|
isnt: is niet
|
|
@@ -12,6 +21,7 @@ nl:
|
|
|
12
21
|
lteq: kleiner dan of gelijk aan
|
|
13
22
|
is_empty: is leeg
|
|
14
23
|
add_filter:
|
|
24
|
+
add: Filter toevoegen
|
|
15
25
|
component:
|
|
16
26
|
add_filter: Filter toevoegen
|
|
17
27
|
filter_pill_list:
|
|
@@ -36,8 +46,22 @@ nl:
|
|
|
36
46
|
available_downloads: Beschikbare downloads
|
|
37
47
|
empty: Je hebt nog geen downloads. Maak er hieronder een aan.
|
|
38
48
|
item_name: "%{table} export"
|
|
49
|
+
repeats_with_interval: "Herhaalt %{interval}"
|
|
50
|
+
repeat_section: Herhalen
|
|
51
|
+
repeat: Herhalen
|
|
52
|
+
repeat_once: "Eenmalig"
|
|
53
|
+
repeat_repeating: Herhalend
|
|
54
|
+
repeat_intervals:
|
|
55
|
+
daily: dagelijks
|
|
56
|
+
weekly: wekelijks
|
|
57
|
+
monthly: maandelijks
|
|
58
|
+
quarterly: per kwartaal
|
|
59
|
+
bi-yearly: halfjaarlijks
|
|
60
|
+
yearly: jaarlijks
|
|
39
61
|
download: Downloaden
|
|
62
|
+
delete: Export verwijderen
|
|
40
63
|
processing: Bezig…
|
|
64
|
+
waiting: Wachtend
|
|
41
65
|
failed: Mislukt
|
|
42
66
|
new_export: Nieuwe export
|
|
43
67
|
scope_all: Alle records (volgens huidige filters)
|
data/config/routes.rb
CHANGED
|
@@ -4,7 +4,7 @@ Mensa::Engine.routes.draw do
|
|
|
4
4
|
resources :filters
|
|
5
5
|
resources :views, only: [:create, :update, :destroy]
|
|
6
6
|
resources :batch_actions, only: [:create]
|
|
7
|
-
resources :exports, only: [:index, :create] do
|
|
7
|
+
resources :exports, only: [:index, :create, :destroy] do
|
|
8
8
|
member do
|
|
9
9
|
get :download
|
|
10
10
|
end
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
class AddRepeatToMensaExports < ActiveRecord::Migration[7.1]
|
|
2
|
+
def change
|
|
3
|
+
add_column :mensa_exports, :repeat, :string, null: false, default: ""
|
|
4
|
+
add_column :mensa_exports, :last_repeat_run_at, :datetime
|
|
5
|
+
add_index :mensa_exports, :repeat
|
|
6
|
+
add_index :mensa_exports, :last_repeat_run_at
|
|
7
|
+
end
|
|
8
|
+
end
|
|
@@ -9,14 +9,35 @@ Mensa.setup do |config|
|
|
|
9
9
|
|
|
10
10
|
# Override icons in use
|
|
11
11
|
config.icons = {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
add_filter_trigger: "fa-solid fa-circle-plus",
|
|
13
|
+
add_filter_selected: "fal fa-check",
|
|
14
|
+
column_customizer_toggle: "fa-solid fa-table-columns",
|
|
15
|
+
column_customizer_handle: "fa-solid fa-grip-vertical",
|
|
16
|
+
column_customizer_visibility_on: "fa-solid fa-eye",
|
|
17
|
+
column_customizer_visibility_off: "fa-solid fa-eye-slash",
|
|
18
|
+
control_bar_reset: "fa-solid fa-rotate-left",
|
|
19
|
+
control_bar_save_dropdown: "fa-solid fa-chevron-down",
|
|
20
|
+
control_bar_view_filters_show: "fa-solid fa-eye",
|
|
21
|
+
control_bar_view_filters_hide: "fa-solid fa-eye-slash",
|
|
18
22
|
control_bar_export: "fa-solid fa-file-export",
|
|
19
|
-
|
|
20
|
-
|
|
23
|
+
empty_state_icon: "fa-solid fa-magnifying-glass",
|
|
24
|
+
filter_pill_remove: "fa-solid fa-xmark",
|
|
25
|
+
filter_pill_list_search: "fa-solid fa-magnifying-glass",
|
|
26
|
+
filter_pill_list_clear: "fa-solid fa-xmark",
|
|
27
|
+
header_order_indicator_asc: "fa-solid fa-arrow-up",
|
|
28
|
+
header_order_indicator_desc: "fa-solid fa-arrow-down",
|
|
29
|
+
views_trigger: "fa-solid fa-sort",
|
|
30
|
+
views_option_selected: "fa-solid fa-check",
|
|
31
|
+
views_option_menu: "fa-solid fa-ellipsis",
|
|
32
|
+
views_option_system: "fa-solid fa-ban",
|
|
33
|
+
views_rename: "fa-solid fa-pencil",
|
|
34
|
+
views_duplicate: "fa-solid fa-copy",
|
|
35
|
+
views_delete: "fa-solid fa-trash",
|
|
36
|
+
exports_dialog_close: "fa-solid fa-xmark",
|
|
37
|
+
exports_dialog_submit: "fa-solid fa-file-export",
|
|
38
|
+
exports_list_delete: "fa-solid fa-trash",
|
|
39
|
+
exports_list_download: "fa-solid fa-download",
|
|
40
|
+
exports_list_processing: "fa-solid fa-spinner fa-spin",
|
|
41
|
+
tables_standard_error: "fa-solid fa-circle-exclamation"
|
|
21
42
|
}
|
|
22
43
|
end
|