diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index b127181cf..0a906477f 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -197,6 +197,11 @@ module ApplicationHelper
simple_form_for(:search, method: method, url: url, defaults: defaults, html: html_options, &block)
end
+ def table_for(*options, &block)
+ table = TableBuilder.new(*options, &block)
+ render "table_builder/table", table: table
+ end
+
def body_attributes(user = CurrentUser.user)
attributes = %i[id name level level_string theme] + User::BOOLEAN_ATTRIBUTES.map(&:to_sym)
attributes += User::Roles.map { |role| :"is_#{role}?" }
diff --git a/app/logical/table_builder.rb b/app/logical/table_builder.rb
new file mode 100644
index 000000000..2dcc672cb
--- /dev/null
+++ b/app/logical/table_builder.rb
@@ -0,0 +1,38 @@
+class TableBuilder
+ class Column
+ attr_reader :attribute, :name, :block, :html_attributes
+
+ def initialize(attribute = nil, name: attribute.to_s.titleize, **html_attributes, &block)
+ @attribute = attribute
+ @html_attributes = html_attributes
+ @name = name
+ @block = block
+ end
+
+ def value(item)
+ if block.present?
+ block.call(item, self)
+ nil
+ else
+ item.send(attribute)
+ end
+ end
+ end
+
+ attr_reader :columns, :html_attributes, :items
+
+ def initialize(items, **html_attributes)
+ @items = items
+ @columns = []
+ @html_attributes = html_attributes
+ yield self if block_given?
+ end
+
+ def column(*options, &block)
+ @columns << Column.new(*options, &block)
+ end
+
+ def row_attributes(item)
+ { id: "#{item.model_name.singular}-#{item.id}", "data-id": item.id }
+ end
+end
diff --git a/app/views/table_builder/_table.html.erb b/app/views/table_builder/_table.html.erb
new file mode 100644
index 000000000..51be7b686
--- /dev/null
+++ b/app/views/table_builder/_table.html.erb
@@ -0,0 +1,21 @@
+<%= tag.table table.html_attributes do %>
+
+
+ <% table.columns.each do |column| %>
+
+
+
+ <%= column.name %>
+ <% end %>
+