From cd3033390c4022abf4fbf6a03acf2ff8b3b1273f Mon Sep 17 00:00:00 2001
From: Sascha Karnatz <68833+kulturbande@users.noreply.github.com>
Date: Sat, 9 Mar 2024 16:01:44 +0100
Subject: [PATCH 01/12] Add resource table component
Add first try of a resource table. It is pretty raw and does not create the right output, but it is only a proposal.
---
.../alchemy/admin/resource/table.rb | 196 ++++++++++++++++++
.../alchemy/admin/resource/table_spec.rb | 187 +++++++++++++++++
2 files changed, 383 insertions(+)
create mode 100644 app/components/alchemy/admin/resource/table.rb
create mode 100644 spec/components/alchemy/admin/resource/table_spec.rb
diff --git a/app/components/alchemy/admin/resource/table.rb b/app/components/alchemy/admin/resource/table.rb
new file mode 100644
index 0000000000..5da94c5fc1
--- /dev/null
+++ b/app/components/alchemy/admin/resource/table.rb
@@ -0,0 +1,196 @@
+# frozen_string_literal: true
+
+module Alchemy
+ module Admin
+ module Resource
+ # Renders a resource table with columns and buttons
+ #
+ # == Example
+ #
+ # <%= render Alchemy::Admin::Resource::Table.new(@languages, query: @query) do |table| %>
+ # <% table.icon_column "translate-2", style: false %>
+ # <% table.column :name, sortable: true %>
+ # <% table.column :language_code, sortable: true %>
+ # <% table.column :page_layout do |language| %>
+ # <%= Alchemy::Page.human_layout_name(language.page_layout) %>
+ # <% end %>
+ # <% table.delete_button %>
+ # <% table.edit_button %>
+ # <% end %>
+ #
+ # @param [ActiveRecord::Relation] :collection
+ # a collection of Alchemy::Resource objects that are shown in the table
+ # @param [Ransack::Search] :query
+ # The ransack search object to allow sortable table columns
+ # @param [String] :nothing_found_label (Alchemy.t("Nothing found"))
+ # The message that will be shown, if the collection is empty
+ # @param [Hash] :search_filter_params ({})
+ # An additional hash that will attached to the delete and edit button to redirect back to
+ # the same page of the table
+ # @param [String] :icon (nil)
+ # a default icon, if the table is auto generated
+ class Table < ViewComponent::Base
+ delegate :can?,
+ :sort_link,
+ :render_attribute,
+ :resource_path,
+ :render_icon,
+ :edit_resource_path,
+ :resource_handler,
+ :resource_window_size,
+ to: :helpers
+
+ attr_reader :buttons,
+ :columns,
+ :collection,
+ :query,
+ :nothing_found_label,
+ :search_filter_params
+
+ erb_template <<~ERB
+ <% if collection.any? %>
+
+
+
+ <% columns.each do |column| %>
+
+ <% if column.sortable %>
+ <%= sort_link query,
+ column.name,
+ column.label,
+ default_order: column.type.to_s =~ /date|time/ ? 'desc' : 'asc' %>
+ <% else %>
+ <%= column.label %>
+ <% end %>
+ |
+ <% end %>
+ <% if buttons.present? %>
+ |
+ <% end %>
+
+
+
+ <% collection.each do |row| %>
+
+ <% columns.each do |column| %>
+
+ <%= view_context.capture(row, &column.block) %>
+ |
+ <% end %>
+ <% if buttons.present? %>
+
+ <% buttons.each do |button| %>
+ <% if button.name.nil? || can?(button.name, row) %>
+ <% if button.tooltip.present? %>
+
+ <%= view_context.capture(row, &button.block) %>
+
+ <% else %>
+ <%= view_context.capture(row, &button.block) %>
+ <% end %>
+ <% end %>
+ <% end %>
+ |
+ <% end %>
+
+ <% end %>
+
+
+ <% else %>
+
+ <%= nothing_found_label %>
+
+ <% end %>
+ ERB
+
+ def initialize(collection, query: nil, nothing_found_label: Alchemy.t("Nothing found"), search_filter_params: {}, icon: nil)
+ @collection = collection
+ @query = query
+ @nothing_found_label = nothing_found_label
+ @search_filter_params = search_filter_params
+ @columns = []
+ @buttons = []
+ @icon = icon
+ end
+
+ def column(name, label: nil, sortable: false, type: nil, class_name: nil, &block)
+ label ||= resource_handler.model.human_attribute_name(name)
+ type ||= resource_handler.model.columns_hash[name.to_s]&.type
+ attribute = resource_handler.attributes.find { |item| item[:name] == name.to_s } || {name: name, type: type}
+ block ||= lambda { |item| render_attribute(item, attribute) }
+
+ @columns << Attribute.new(name, label: label, sortable: sortable, type: type, alignment: class_name, &block)
+ end
+
+ def icon_column(icon = nil, style: nil)
+ @columns << Attribute.new(:icon, label: "") do |row|
+ render_icon(icon || yield(row), size: "xl", style: style)
+ end
+ end
+
+ def button(name = nil, tooltip: nil, &block)
+ @buttons << Button.new(name, tooltip: tooltip, &block)
+ end
+
+ def delete_button(tooltip: Alchemy.t("Delete"), message: Alchemy.t("Are you sure?"))
+ button(:destroy, tooltip: tooltip) do |row|
+ helpers.delete_button(resource_path(row, search_filter_params), {message: message})
+ end
+ end
+
+ def edit_button(tooltip: Alchemy.t("Edit"), title: Alchemy.t("Edit"), size: resource_window_size)
+ button(:edit, tooltip: tooltip) do |row|
+ helpers.link_to_dialog render_icon(:edit),
+ edit_resource_path(row, search_filter_params),
+ {
+ title: title,
+ size: size
+ },
+ class: "icon_button"
+ end
+ end
+
+ private
+
+ ##
+ # the before_render - method is necessary to force ViewComponent to evaluate the column - calls
+ # if no columns or buttons are available the resource_helper will be used, to generate the
+ # default attributes of the given resource
+ def before_render
+ content
+ if columns.empty? && buttons.empty?
+ icon_column(@icon) if @icon.present?
+ resource_handler.sorted_attributes.each do |attribute|
+ column(attribute[:name], sortable: true)
+ end
+ delete_button
+ edit_button
+ end
+ end
+
+ class Attribute
+ attr_reader :block, :label, :name, :sortable, :type, :css_classes
+
+ def initialize(name, sortable: false, label: nil, type: :string, alignment: nil, &block)
+ @name = name
+ @label = label || name
+ @sortable = sortable
+ @block = block
+ @type = type
+ @css_classes = [name, type, alignment].compact.join(" ")
+ end
+ end
+
+ class Button
+ attr_reader :name, :tooltip, :block
+
+ def initialize(name = nil, tooltip: nil, &block)
+ @name = name
+ @tooltip = tooltip
+ @block = block
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/components/alchemy/admin/resource/table_spec.rb b/spec/components/alchemy/admin/resource/table_spec.rb
new file mode 100644
index 0000000000..02f9c1fdbe
--- /dev/null
+++ b/spec/components/alchemy/admin/resource/table_spec.rb
@@ -0,0 +1,187 @@
+# frozen_string_literal: true
+
+require "rails_helper"
+
+CustomResource = Struct.new(:name, :description)
+
+RSpec.describe Alchemy::Admin::Resource::Table, type: :component do
+ let(:collection) { [] }
+ let(:component) { described_class.new(collection) }
+
+ subject(:render) do
+ with_controller_class(Admin::EventsController) do
+ render_inline(component)
+ end
+ end
+
+ context "with data" do
+ let(:collection) {
+ [
+ CustomResource.new("Foo", "Awesome description"),
+ CustomResource.new("Bar", "Another description")
+ ]
+ }
+
+ context "columns without block" do
+ subject(:render) do
+ with_controller_class(Admin::EventsController) do
+ render_inline(component) do |table|
+ table.column(:name)
+ table.column(:description)
+ end
+ end
+ end
+
+ before do
+ render
+ end
+
+ it "renders a table header" do
+ expect(page).to have_selector("table th", text: "Name")
+ expect(page).to have_selector("table th", text: "Description")
+ end
+
+ it "renders a table cell" do
+ expect(page).to have_selector("table td.name", text: "Foo")
+ expect(page).to have_selector("table td.description", text: "Awesome description")
+ end
+ end
+
+ context "columns with custom label" do
+ subject(:render) do
+ with_controller_class(Admin::EventsController) do
+ render_inline(component) do |component|
+ component.column(:name, label: "Awesome Name")
+ end
+ end
+ end
+
+ it "renders a table header with custom label" do
+ render
+ expect(page).to have_selector("table th", text: "Awesome Name")
+ end
+ end
+
+ context "columns with a custom block" do
+ subject(:render) do
+ with_controller_class(Admin::EventsController) do
+ render_inline(component) do |table|
+ table.column(:description) { |item| item[:description].truncate(10) }
+ end
+ end
+ end
+
+ it "renders a table cell with a custom block" do
+ render
+ expect(page).to have_selector("table td", text: "Awesome...")
+ end
+ end
+
+ context "columns with a custom block" do
+ subject(:render) do
+ with_controller_class(Admin::EventsController) do
+ render_inline(component) do |table|
+ table.column(:description, class_name: "fooooo")
+ end
+ end
+ end
+
+ it "renders a table cell with given class" do
+ render
+ expect(page).to have_selector("table td.fooooo")
+ end
+ end
+
+ context "icon column with variable" do
+ subject(:render) do
+ with_controller_class(Admin::EventsController) do
+ render_inline(component) do |table|
+ table.icon_column(:home)
+ end
+ end
+ end
+
+ it "renders a table cell with a home icon" do
+ render
+ expect(page).to have_selector("table td alchemy-icon[name='home']")
+ end
+ end
+
+ context "icon column with custom block" do
+ subject(:render) do
+ with_controller_class(Admin::EventsController) do
+ render_inline(component) do |table|
+ table.icon_column { |row| (row[:name] == "Foo") ? :save : :home }
+ end
+ end
+ end
+
+ it "renders a table cell with a home icon another one with a save icon" do
+ render
+ expect(page).to have_selector("table td alchemy-icon[name='save']")
+ expect(page).to have_selector("table td alchemy-icon[name='home']")
+ end
+ end
+
+ context "buttons" do
+ let(:name) { nil }
+ let(:tooltip) { nil }
+
+ subject(:render) do
+ with_controller_class(Admin::EventsController) do
+ render_inline(component) do |table|
+ table.button(name, tooltip: tooltip) { |row| "Foo" }
+ end
+ end
+ end
+
+ before do
+ render
+ end
+
+ context "button without any config" do
+ it "renders an button entry" do
+ expect(page).to have_selector("table td.tools", text: "Foo")
+ end
+
+ it "does not render a tooltip without tooltip config" do
+ expect(page).to_not have_selector("table td.tools sl-tooltip")
+ end
+ end
+
+ context "with tooltip" do
+ let(:tooltip) { "Bar" }
+
+ it "does render a tooltip without tooltip config" do
+ expect(page).to have_selector("table td.tools sl-tooltip[content='Bar']")
+ end
+ end
+
+ context "with permission" do
+ let(:name) { :unknown_permission }
+
+ it "does not renders a button entry" do
+ expect(page).to_not have_selector("table td.tools", text: "Foo")
+ end
+ end
+ end
+ end
+
+ context "without any data" do
+ before do
+ render
+ end
+
+ it "renders an info message" do
+ expect(page).to have_content("Nothing found")
+ end
+
+ context "with another nothing found - label" do
+ let(:component) { described_class.new(collection, nothing_found_label: "No user found") }
+
+ it "renders an info message" do
+ expect(page).to have_content("No user found")
+ end
+ end
+ end
+end
From ac8c61421c8ae7f416369087a99c23a3b64dae9d Mon Sep 17 00:00:00 2001
From: Sascha Karnatz <68833+kulturbande@users.noreply.github.com>
Date: Sun, 21 Jul 2024 23:16:34 +0200
Subject: [PATCH 02/12] Replace resource table partial with resource component
Use the resource table - component for the default resource views.
---
.../alchemy/admin/resources/_table.html.erb | 26 +------------------
1 file changed, 1 insertion(+), 25 deletions(-)
diff --git a/app/views/alchemy/admin/resources/_table.html.erb b/app/views/alchemy/admin/resources/_table.html.erb
index 68d601af98..b19560223a 100644
--- a/app/views/alchemy/admin/resources/_table.html.erb
+++ b/app/views/alchemy/admin/resources/_table.html.erb
@@ -1,27 +1,3 @@
-<% if resources_instance_variable.any? %>
-
-
-
- <% if local_assigns[:icon] %>
- |
- <% end %>
- <% resource_handler.sorted_attributes.each do |attribute| %>
-
- <%= sort_link [:resource_url_proxy, @query],
- sortable_resource_header_column(attribute),
- resource_handler.model.human_attribute_name(attribute[:name]),
- default_order: attribute[:type].to_s =~ /date|time/ ? 'desc' : 'asc' %>
- |
- <% end %>
- |
-
-
-
- <%= render_resources(icon: local_assigns[:icon]) %>
-
-
-<% elsif search_filter_params[:q].present? %>
-<%= Alchemy.t('Nothing found') %>
-<% end %>
+<%= render Alchemy::Admin::Resource::Table.new(resources_instance_variable, query: @query, search_filter_params: search_filter_params, icon: local_assigns[:icon]) %>
<%= paginate resources_instance_variable, scope: resource_url_proxy, theme: 'alchemy' %>
From db81c04ddf52d19b10a9def93d6c3d3e9fe5776b Mon Sep 17 00:00:00 2001
From: Sascha Karnatz <68833+kulturbande@users.noreply.github.com>
Date: Mon, 22 Jul 2024 10:40:08 +0200
Subject: [PATCH 03/12] Use table component in language index - view
Use the new table component for the language index - view and remove the old implementation.
---
.../admin/languages/_language.html.erb | 50 ----------------
.../alchemy/admin/languages/_table.html.erb | 58 +++++--------------
2 files changed, 16 insertions(+), 92 deletions(-)
delete mode 100644 app/views/alchemy/admin/languages/_language.html.erb
diff --git a/app/views/alchemy/admin/languages/_language.html.erb b/app/views/alchemy/admin/languages/_language.html.erb
deleted file mode 100644
index 40dc24230d..0000000000
--- a/app/views/alchemy/admin/languages/_language.html.erb
+++ /dev/null
@@ -1,50 +0,0 @@
-
-
- <%= render_icon("translate-2", size: "xl", style: false) %>
- |
-
- <%= language.name %>
- |
-
- <%= language.language_code %>
- |
-
- <%= language.country_code %>
- |
-
- <%= language.code %>
- |
-
- <%= language.locale %>
- |
-
- <%= language.frontpage_name %>
- |
-
- <%= Alchemy::Page.human_layout_name(language.page_layout) %>
- |
-
- <%= language.public? ? render_icon(:check) : nil %>
- |
-
- <%= language.default? ? render_icon(:check) : nil %>
- |
-
- <%- if can?(:destroy, language) -%>
- ">
- <%= delete_button resource_path(language) %>
-
- <%- end -%>
- <%- if can?(:edit, language) -%>
- " placement="top-end">
- <%= link_to_dialog render_icon(:edit),
- alchemy.edit_admin_language_path(language),
- {
- title: Alchemy.t("Edit"),
- size: "430x415"
- },
- class: "icon_button" %>
-
- <%- end -%>
- |
-
diff --git a/app/views/alchemy/admin/languages/_table.html.erb b/app/views/alchemy/admin/languages/_table.html.erb
index 63ff2d869b..8e0c02150d 100644
--- a/app/views/alchemy/admin/languages/_table.html.erb
+++ b/app/views/alchemy/admin/languages/_table.html.erb
@@ -1,44 +1,18 @@
-<%- if @languages.any? -%>
-
-
-
- |
-
- <%= sort_link @query, :name %>
- |
-
- <%= sort_link @query, :language_code %>
- |
-
- <%= sort_link @query, :country_code %>
- |
-
- <%= Alchemy::Language.human_attribute_name(:code) %>
- |
-
- <%= Alchemy::Language.human_attribute_name(:locale) %>
- |
-
- <%= Alchemy::Language.human_attribute_name(:frontpage_name) %>
- |
-
- <%= Alchemy::Language.human_attribute_name(:page_layout) %>
- |
-
- <%= Alchemy::Language.human_attribute_name(:public) %>
- |
-
- <%= Alchemy::Language.human_attribute_name(:default) %>
- |
- |
-
-
-
- <%= render_resources %>
-
-
-<%- elsif search_filter_params[:q].present? -%>
-<%= Alchemy.t('Nothing found') %>
-<%- end -%>
+<%= render Alchemy::Admin::Resource::Table.new(@languages, query: @query, search_filter_params: search_filter_params) do |table| %>
+ <% table.icon_column "translate-2", style: false %>
+ <% table.column :name, sortable: true %>
+ <% table.column :language_code, sortable: true %>
+ <% table.column :country_code %>
+ <% table.column :code %>
+ <% table.column :locale %>
+ <% table.column :frontpage_name %>
+ <% table.column :page_layout do |row| %>
+ <%= Alchemy::Page.human_layout_name(row.page_layout) %>
+ <% end %>
+ <% table.column :public %>
+ <% table.column :default %>
+ <% table.delete_button %>
+ <% table.edit_button %>
+<% end %>
<%= paginate @languages, theme: 'alchemy' %>
From 91f29cca23856a9bf3e4bc70c4012a19b79c778f Mon Sep 17 00:00:00 2001
From: Sascha Karnatz <68833+kulturbande@users.noreply.github.com>
Date: Tue, 23 Jul 2024 10:02:52 +0200
Subject: [PATCH 04/12] Add boolean value to render_attribute in resource
helper
Draw a check - icon if the value is boolean and is truthy. This was done previously in the view, but it is now easier to reuse these behavior in the table component.
---
lib/alchemy/resources_helper.rb | 2 ++
spec/libraries/resources_helper_spec.rb | 27 +++++++++++++++++++++++++
2 files changed, 29 insertions(+)
diff --git a/lib/alchemy/resources_helper.rb b/lib/alchemy/resources_helper.rb
index 102e3946c4..0a71ba92cd 100644
--- a/lib/alchemy/resources_helper.rb
+++ b/lib/alchemy/resources_helper.rb
@@ -87,6 +87,8 @@ def render_attribute(resource, attribute, options = {})
options[:time_format] || :"alchemy.time"
end
value = l(attribute_value, format: localization_format)
+ elsif attribute[:type] == :boolean
+ value = attribute_value ? ''.html_safe : nil
else
value = attribute_value
end
diff --git a/spec/libraries/resources_helper_spec.rb b/spec/libraries/resources_helper_spec.rb
index 0077cb5736..39876bddd7 100644
--- a/spec/libraries/resources_helper_spec.rb
+++ b/spec/libraries/resources_helper_spec.rb
@@ -240,6 +240,33 @@ def resource_handler
end
end
end
+
+ context "format of boolean values" do
+ let(:attributes) do
+ {
+ name: :foo,
+ type: :boolean
+ }
+ end
+
+ let(:enabled) { true }
+
+ before do
+ allow(resource_item).to receive(:foo) { enabled }
+ end
+
+ it "should respond with a check icon" do
+ expect(subject).to eq("")
+ end
+
+ context "disabled attribute" do
+ let(:enabled) { false }
+
+ it "should show nothing" do
+ expect(subject).to eq("")
+ end
+ end
+ end
end
describe "#render_resources" do
From 0c5a85217d7d5d9637ff0b4bcbb6c3871337def4 Mon Sep 17 00:00:00 2001
From: Sascha Karnatz <68833+kulturbande@users.noreply.github.com>
Date: Tue, 23 Jul 2024 14:51:32 +0200
Subject: [PATCH 05/12] Use controller_path to find alchemy module in resource
controller
The params - hash is at least in test environments not always set and it is better to use the default Rails methods to get the path to the controller.
---
app/controllers/alchemy/admin/resources_controller.rb | 2 +-
spec/controllers/alchemy/admin/resources_controller_spec.rb | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/app/controllers/alchemy/admin/resources_controller.rb b/app/controllers/alchemy/admin/resources_controller.rb
index 8e57d988e9..b61b20726a 100644
--- a/app/controllers/alchemy/admin/resources_controller.rb
+++ b/app/controllers/alchemy/admin/resources_controller.rb
@@ -170,7 +170,7 @@ def is_alchemy_module?
end
def alchemy_module
- @alchemy_module ||= module_definition_for(controller: params[:controller], action: "index")
+ @alchemy_module ||= module_definition_for(controller: controller_path, action: "index")
end
def load_resource
diff --git a/spec/controllers/alchemy/admin/resources_controller_spec.rb b/spec/controllers/alchemy/admin/resources_controller_spec.rb
index 7f4ef17e7e..8efb78e8fb 100644
--- a/spec/controllers/alchemy/admin/resources_controller_spec.rb
+++ b/spec/controllers/alchemy/admin/resources_controller_spec.rb
@@ -99,7 +99,7 @@ def resource_handler
let(:params) { {q: {name_cont: "some_query"}, page: 6} }
it "redirects to index, keeping the current location parameters" do
- expect(controller).to receive(:controller_path) { "admin/series" }
+ expect(controller).to receive(:controller_path).twice { "admin/series" }
post :update, params: {id: peter.id, series: {name: "Hans"}}.merge(params)
expect(response.redirect_url).to eq("http://test.host/admin/series?page=6&q%5Bname_cont%5D=some_query")
end
@@ -133,7 +133,7 @@ def resource_handler
let(:params) { {q: {name_cont: "some_query"}, page: 6} }
it "redirects to index, keeping the current location parameters" do
- expect(controller).to receive(:controller_path) { "admin/series" }
+ expect(controller).to receive(:controller_path).twice { "admin/series" }
post :create, params: {series: {name: "Hans"}}.merge(params)
expect(response.redirect_url).to eq("http://test.host/admin/series?page=6&q%5Bname_cont%5D=some_query")
end
From 1d935fb97636f412cdaf506ed6c538944dab9ea8 Mon Sep 17 00:00:00 2001
From: Sascha Karnatz <68833+kulturbande@users.noreply.github.com>
Date: Tue, 23 Jul 2024 19:05:47 +0200
Subject: [PATCH 06/12] Replace attachment table with table component
The implementation is pretty verbose, because the table itself has a lot of extra functionalities. It is not possible to use the default delete and edit buttons, because of different redirect links in the button configuration.
---
.../admin/attachments/_attachment.html.erb | 81 -----------------
.../admin/attachments/_files_list.html.erb | 90 +++++++++++++++----
2 files changed, 74 insertions(+), 97 deletions(-)
delete mode 100644 app/views/alchemy/admin/attachments/_attachment.html.erb
diff --git a/app/views/alchemy/admin/attachments/_attachment.html.erb b/app/views/alchemy/admin/attachments/_attachment.html.erb
deleted file mode 100644
index 96dbd20399..0000000000
--- a/app/views/alchemy/admin/attachments/_attachment.html.erb
+++ /dev/null
@@ -1,81 +0,0 @@
-
-
- <%= render_icon attachment.icon_css_class, size: "xl" %>
- |
-
- <% if can?(:show, attachment) %>
- <%= link_to_dialog(
- attachment.name,
- alchemy.admin_attachment_path(attachment),
- {
- title: attachment.name,
- size: attachment_preview_size(attachment)
- },
- {
- title: Alchemy.t('Attachment Preview')
- }
- ) %>
- <% else %>
- <%= attachment.name %>
- <% end %>
- |
- <%= attachment.file_name %> |
- <%= mime_to_human(attachment.file_mime_type) %> |
- <%= number_to_human_size(attachment.file_size) %> |
- <%= l(attachment.created_at, format: :'alchemy.default') %> |
-
- <% if can?(:show, attachment) %>
-
- <%= link_to_dialog(
- render_icon(:information),
- alchemy.admin_attachment_path(attachment),
- {
- title: attachment.name,
- size: attachment_preview_size(attachment)
- },
- class: "icon_button"
- ) %>
-
- <% end %>
- <% if can?(:download, attachment) %>
- ">
- <%= link_to render_icon(:download),
- alchemy.download_admin_attachment_path(attachment),
- target: "_blank",
- class: "icon_button" %>
-
- <% end %>
- <% if can?(:edit, attachment) %>
-
- <%= render 'alchemy/admin/attachments/replace_button',
- redirect_url: alchemy.admin_attachments_path,
- object: attachment,
- file_attribute: 'file' %>
-
- <% end %>
- <% if can?(:destroy, attachment) %>
-
- <%= link_to_confirm_dialog render_icon(:minus),
- Alchemy.t(:confirm_to_delete_file),
- alchemy.admin_attachment_path(
- id: attachment,
- q: search_filter_params[:q],
- page: params[:page],
- per_page: params[:per_page]
- ),
- class: "icon_button" %>
-
- <% end %>
- <% if can?(:edit, attachment) %>
-
- <%= link_to_dialog render_icon(:edit),
- alchemy.edit_admin_attachment_path(attachment, q: search_filter_params[:q], page: params[:page]),
- {
- title: Alchemy.t(:rename_file),
- size: '500x250'
- },
- class: "icon_button" %>
-
- <% end %>
- |
-
diff --git a/app/views/alchemy/admin/attachments/_files_list.html.erb b/app/views/alchemy/admin/attachments/_files_list.html.erb
index b184d5ecd1..8010227e90 100644
--- a/app/views/alchemy/admin/attachments/_files_list.html.erb
+++ b/app/views/alchemy/admin/attachments/_files_list.html.erb
@@ -7,22 +7,80 @@
<% end %>
<% end %>
<% else %>
-
-
-
- |
- <%= sort_link(@query, :name) %> |
- <%= sort_link(@query, :file_name) %> |
- <%= Alchemy::Attachment.human_attribute_name('file_mime_type') %> |
- <%= sort_link(@query, :file_size) %> |
- <%= sort_link(@query, :created_at, default_order: 'desc') %> |
- |
-
-
-
- <%= render partial: 'alchemy/admin/attachments/attachment', collection: @attachments %>
-
-
+ <%= render Alchemy::Admin::Resource::Table.new(@attachments, query: @query) do |table| %>
+ <% table.icon_column { |attachment| attachment.icon_css_class } %>
+ <% table.column :name, sortable: true do |attachment| %>
+ <% if can?(:show, attachment) %>
+ <%= link_to_dialog(
+ attachment.name,
+ alchemy.admin_attachment_path(attachment),
+ {
+ title: attachment.name,
+ size: attachment_preview_size(attachment)
+ },
+ {
+ title: Alchemy.t('Attachment Preview')
+ }
+ ) %>
+ <% else %>
+ <%= attachment.name %>
+ <% end %>
+ <% end %>
+ <% table.column :file_name, sortable: true %>
+ <% table.column :file_mime_type do |attachment| %>
+ <%= mime_to_human(attachment.file_mime_type) %>
+ <% end %>
+ <% table.column :file_size, sortable: true do |attachment| %>
+ <%= number_to_human_size(attachment.file_size) %>
+ <% end %>
+ <% table.column :created_at, sortable: true %>
+
+ <% table.button(:show, tooltip: Alchemy.t('Attachment Preview')) do |attachment| %>
+ <%= link_to_dialog(
+ render_icon(:information),
+ alchemy.admin_attachment_path(attachment),
+ {
+ title: attachment.name,
+ size: attachment_preview_size(attachment)
+ },
+ class: "icon_button"
+ ) %>
+ <% end %>
+ <% table.button(:download) do |attachment| %>
+ ">
+ <%= link_to render_icon(:download),
+ alchemy.download_admin_attachment_path(attachment),
+ target: "_blank",
+ class: "icon_button" %>
+
+ <% end %>
+ <% table.button(:edit, tooltip: Alchemy.t(:replace_file)) do |attachment| %>
+ <%= render 'alchemy/admin/attachments/replace_button',
+ redirect_url: alchemy.admin_attachments_path,
+ object: attachment,
+ file_attribute: 'file' %>
+ <% end %>
+ <% table.button(:destroy, tooltip: Alchemy.t(:delete_file)) do |attachment| %>
+ <%= link_to_confirm_dialog render_icon(:minus),
+ Alchemy.t(:confirm_to_delete_file),
+ alchemy.admin_attachment_path(
+ id: attachment,
+ q: search_filter_params[:q],
+ page: params[:page],
+ per_page: params[:per_page]
+ ),
+ class: "icon_button" %>
+ <% end %>
+ <% table.button(:edit, tooltip: Alchemy.t(:rename_file)) do |attachment| %>
+ <%= link_to_dialog render_icon(:edit),
+ alchemy.edit_admin_attachment_path(attachment, q: search_filter_params[:q], page: params[:page]),
+ {
+ title: Alchemy.t(:rename_file),
+ size: '500x250'
+ },
+ class: "icon_button" %>
+ <% end %>
+ <% end %>
<%= paginate @attachments, theme: 'alchemy' %>
<% end %>
From 45af15453a2e9d25f39612a92ea7699be992dc1c Mon Sep 17 00:00:00 2001
From: Sascha Karnatz <68833+kulturbande@users.noreply.github.com>
Date: Wed, 24 Jul 2024 09:46:08 +0200
Subject: [PATCH 07/12] Replace page table partial with table component
Use the new table component also for the page table. This table also has a lot of custom logic inside, that makes the configuration a bit verbose.
---
app/views/alchemy/admin/pages/_table.html.erb | 119 ++++++++++++++----
.../alchemy/admin/pages/_table_row.html.erb | 111 ----------------
.../alchemy/admin/pages/list/_table.html.erb | 31 -----
3 files changed, 92 insertions(+), 169 deletions(-)
delete mode 100644 app/views/alchemy/admin/pages/_table_row.html.erb
delete mode 100644 app/views/alchemy/admin/pages/list/_table.html.erb
diff --git a/app/views/alchemy/admin/pages/_table.html.erb b/app/views/alchemy/admin/pages/_table.html.erb
index f2a71a8e98..cace9df5d2 100644
--- a/app/views/alchemy/admin/pages/_table.html.erb
+++ b/app/views/alchemy/admin/pages/_table.html.erb
@@ -1,30 +1,95 @@
-
-
-
- |
-
- <%= sort_link [:alchemy, @query],
- "name",
- Alchemy::Page.human_attribute_name(:name),
- default_order: "asc" %>
- |
- <%= Alchemy::Page.human_attribute_name(:urlname) %> |
- <%= Alchemy::Page.human_attribute_name(:page_type) %> |
- <%= Alchemy::Page.human_attribute_name(:tag_list) %> |
-
- <%= sort_link [:alchemy, @query],
- :updated_at,
- Alchemy::Page.human_attribute_name(:updated_at),
- default_order: "desc" %>
- |
- <%= Alchemy::Page.human_attribute_name(:status) %> |
- |
-
-
-
- <%= render partial: "table_row", collection: @pages, as: "page" %>
-
-
+<%= render Alchemy::Admin::Resource::Table.new(@pages, query: @query, search_filter_params: search_filter_params) do |table| %>
+ <% table.column :icon, label: "" do |page| %>
+ <% if can?(:edit_content, page) %>
+ <% if page.locked? %>
+ " placement="bottom-start">
+ <%= render_icon "file-edit", size: "xl" %>
+
+ <% else %>
+ <%= render_icon "file-edit", size: "xl" %>
+ <% end %>
+ <% else %>
+ " placement="bottom-start">
+ <%= render_icon "file-forbid", size: "xl" %>
+
+ <% end %>
+ <% end %>
+ <% table.column :name, sortable: true do |page| %>
+ <%= link_to_if(
+ can?(:edit_content, page),
+ page.name,
+ alchemy.edit_admin_page_path(page),
+ title: Alchemy.t(:edit_page),
+ ) { content_tag(:span, page.name) } -%>
+ <% end %>
+ <% table.column :url_path, label: Alchemy::Page.human_attribute_name(:urlname) %>
+ <% table.column :page_type do |page| %>
+ <%= Alchemy.t(page.page_layout, scope: "page_layout_names", default: page.page_layout.to_s.humanize) %>
+ <% end %>
+ <% table.column :tag_list do |page| %>
+ <% page.tag_list.each do |tag| %>
+ <%= content_tag(:span, tag, class: "tag") %>
+ <% end %>
+ <% end %>
+ <% table.column :updated_at, sortable: true %>
+ <% table.column :status, alignment: :right do |page| %>
+ <% if page.locked? %>
+
+ <%= render_icon(:edit, size: "1x") %>
+ <%= page.status_title(:locked) %>
+
+ <% end %>
+ <% if page.restricted? %>
+
+ <%= render_icon(:lock, size: "1x") %>
+ <%= page.status_title(:restricted) %>
+
+ <% end %>
+ <% unless page.public? %>
+
+ <%= render_icon("cloud-off", size: "1x") %>
+ <%= page.status_title(:public) %>
+
+ <% end %>
+ <% end %>
+
+ <% table.button :info, tooltip: Alchemy.t(:page_infos) do |page| %>
+ <%= link_to_dialog(
+ render_icon('info-circle'),
+ alchemy.info_admin_page_path(page),
+ {
+ title: Alchemy.t(:page_infos),
+ size: '520x290'
+ },
+ class: "icon_button"
+ ) %>
+ <% end %>
+ <% table.button :configure, tooltip: Alchemy.t(:edit_page_properties) do |page| %>
+ <%= link_to_dialog(
+ render_icon(:cog),
+ alchemy.configure_admin_page_path(page),
+ {
+ title: Alchemy.t(:edit_page_properties),
+ size: '450x680'
+ },
+ class: "icon_button"
+ ) -%>
+ <% end %>
+ <% table.button :copy, tooltip: Alchemy.t(:copy_page) do |page| %>
+ <%= link_to(
+ render_icon(:copy),
+ alchemy.insert_admin_clipboard_path(
+ remarkable_type: :pages,
+ remarkable_id: page.id,
+ ),
+ remote: true,
+ method: :post,
+ class: "icon_button"
+ ) %>
+ <% end %>
+ <% table.delete_button tooltip: Alchemy.t(:delete_page), message: Alchemy.t(:confirm_to_delete_page) %>
+<% end %>
+