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| %> + + <% end %> + <% if buttons.present? %> + + <% end %> + + + + <% collection.each do |row| %> + + <% columns.each do |column| %> + + <% end %> + <% if buttons.present? %> + + <% end %> + + <% end %> + +
+ <% if column.sortable %> + <%= sort_link query, + column.name, + column.label, + default_order: column.type.to_s =~ /date|time/ ? 'desc' : 'asc' %> + <% else %> + <%= column.label %> + <% end %> +
+ <%= view_context.capture(row, &column.block) %> + + <% 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 %> +
+ <% 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| %> - - <% end %> - - - - - <%= render_resources(icon: local_assigns[:icon]) %> - -
- <%= 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' %> -
-<% 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? -%> - - - - - - - - - - - - - - - - - - <%= render_resources %> - -
- <%= 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) %> -
-<%- 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 %> - - - - - - - - - - - - - - <%= render partial: 'alchemy/admin/attachments/attachment', collection: @attachments %> - -
<%= 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 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 @@ - - - - - - - - - - - - - - - <%= render partial: "table_row", collection: @pages, as: "page" %> - -
- <%= 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 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 %> +