Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[pull] main from solidusio:main #368

Merged
merged 24 commits into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
68915c7
Remove order promotions promotion code value from search key
rainerdema Sep 20, 2023
679001c
Include search_param in `ui/table` component
rainerdema Sep 28, 2023
c3744d4
Update Stimulus actions for improved search behavior
rainerdema Sep 28, 2023
1850b99
Round table borders directly instead of relying overflow:hidden
elia Oct 10, 2023
53e5b45
Introduce dynamic ransack filter component for complex queries
elia Oct 10, 2023
01f75cf
Implement dynamic checkboxes sorting within options
rainerdema Sep 28, 2023
5f9aad1
Add click outside to close details
rainerdema Sep 28, 2023
2f41a2b
Introduce dynamic search bar when selections are more than 6
rainerdema Sep 28, 2023
f48eef8
Initialize checkboxes based on URL parameters
rainerdema Sep 28, 2023
983dcc9
Highlight the filter if there are selected options
rainerdema Sep 28, 2023
ca9e9f8
Introduce new filters for the products index
rainerdema Sep 28, 2023
58d4bda
Introduce new filters for the orders index
rainerdema Sep 28, 2023
0d54847
Refactor event dispatching in Stimulus controllers
rainerdema Sep 28, 2023
9fd261a
Let /admin/products/new fallback to the backend
elia Oct 10, 2023
8302a83
Don't show missing order shipment and payment states
elia Oct 10, 2023
ef86996
Enable clickable filter labels for checkboxes
rainerdema Oct 10, 2023
6e78e0a
Add no-options message for empty filter
rainerdema Oct 10, 2023
dc8dad1
Merge pull request #5376 from nebulab/rainerd/add-dropdown-filter-com…
rainerdema Oct 10, 2023
5b24d71
Resolve issue with clickable labels due to spaces in IDs
rainerdema Oct 10, 2023
cfa016a
Merge pull request #5427 from solidusio/elia/admin/order-states
elia Oct 11, 2023
7aec6d0
Merge pull request #5426 from solidusio/elia/admin/misc-fixes
elia Oct 11, 2023
0b2c035
Extract the table search field to a component
elia Oct 4, 2023
ce69e2c
Merge pull request #5429 from nebulab/rainerd/ransack-filter/fix/para…
rainerdema Oct 11, 2023
afc630e
Merge pull request #5428 from solidusio/elia/admin/search-field
elia Oct 11, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 61 additions & 5 deletions admin/app/components/solidus_admin/orders/index/component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,65 @@ def batch_actions
def filters
[
{
name: 'q[completed_at_not_null]',
value: 1,
label: t('.filters.only_show_complete_orders'),
presentation: t('.filters.status'),
combinator: 'or',
attribute: "state",
predicate: "eq",
options: Spree::Order.state_machines[:state].states.map do |state|
[
state.value.titleize,
state.value
]
end
},

{
presentation: t('.filters.shipment_state'),
combinator: 'or',
attribute: "shipment_state",
predicate: "eq",
options: %i[backorder canceled partial pending ready shipped].map do |option|
[
option.to_s.capitalize,
option
]
end
},
{
presentation: t('.filters.payment_state'),
combinator: 'or',
attribute: "payment_state",
predicate: "eq",
options: %i[balance_due checkout completed credit_owed invalid paid pending processing void].map do |option|
[
option.to_s.titleize,
option
]
end
},
{
presentation: t('.filters.variants'),
combinator: 'or',
attribute: "line_items_variant_id",
predicate: "in",
options: Spree::Variant.all.map do |variant|
[
variant.descriptive_name,
variant.id
]
end
},
{
presentation: t('.filters.promotions'),
combinator: 'or',
attribute: "promotions_id",
predicate: "in",
options: Spree::Promotion.all.map do |promotion|
[
promotion.name,
promotion.id
]
end
},
]
end
Expand Down Expand Up @@ -102,7 +158,7 @@ def payment_column
{
header: :payment,
data: ->(order) do
component('ui/badge').new(name: order.payment_state&.humanize, color: order.paid? ? :green : :yellow)
component('ui/badge').new(name: order.payment_state.humanize, color: order.paid? ? :green : :yellow) if order.payment_state?
end
}
end
Expand All @@ -111,7 +167,7 @@ def shipment_column
{
header: :shipment,
data: ->(order) do
component('ui/badge').new(name: order.shipment_state&.humanize, color: order.shipped? ? :green : :yellow)
component('ui/badge').new(name: order.shipment_state.humanize, color: order.shipped? ? :green : :yellow) if order.shipment_state?
end
}
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ en:
one: 1 Item
other: '%{count} Items'
filters:
only_show_complete_orders: Only show complete orders
status: Status
shipment_state: Shipment State
payment_state: Payment State
variants: Variants
promotions: Promotions
date:
formats:
short: '%d %b %y'
19 changes: 13 additions & 6 deletions admin/app/components/solidus_admin/products/index/component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,20 @@ def batch_actions
end

def filters
[
Spree::OptionType.all.map do |option_type|
{
name: 'q[with_discarded]',
value: true,
label: t('.filters.with_deleted'),
},
]
presentation: option_type.presentation,
combinator: 'or',
attribute: "variants_option_values",
predicate: "in",
options: option_type.option_values.map do |option_value|
[
option_value.name,
option_value.id
]
end
}
end
end

def columns
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class SolidusAdmin::UI::Forms::Input::Component < SolidusAdmin::BaseComponent
datetime-local
month
week
search
time
]).freeze

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<label
data-controller="<%= stimulus_id %>"
class="items-center gap-1 p-0 inline-flex w-full justify-start relative"
>
<%= render component("ui/icon").new(
name: "search-line",
class: "w-[1.4em] h-[1.4em] fill-gray-500 absolute ml-3",
) %>
<%= render component("ui/forms/input").new(**@attributes) %>
<button
class="absolute right-0 mr-3 peer-placeholder-shown:hidden"
data-action="<%= stimulus_id %>#clear"
aria-label="<%= t('.clear') %>"
>
<%= render component("ui/icon").new(
name: "close-circle-fill",
class: "w-[1.4em] h-[1.4em] fill-gray-500"
) %>
</button>
</label>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
static targets = ["input"]

clear() {
this.inputTarget.value = ""
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# frozen_string_literal: true

class SolidusAdmin::UI::Forms::SearchField::Component < SolidusAdmin::BaseComponent
def initialize(**attributes)
@attributes = attributes
@attributes[:type] ||= :search
@attributes[:class] = "search-cancel:appearance-none peer !px-10 #{@attributes[:class]}"
@attributes[:"data-#{stimulus_id}-target"] = "input"
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
en:
clear: Clear
65 changes: 26 additions & 39 deletions admin/app/components/solidus_admin/ui/table/component.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,20 @@
rounded-lg
border
border-gray-100
overflow-hidden
"
data-controller="<%= stimulus_id %>"
data-<%= stimulus_id %>-selected-row-class="bg-gray-15"
data-action="
<%= component("ui/table/ransack_filter").stimulus_id %>:search-><%= stimulus_id %>#search
<%= component("ui/table/ransack_filter").stimulus_id %>:showSearch-><%= stimulus_id %>#showSearch
"
>
<% toolbar_classes = "h-14 p-2 bg-white border-b border-gray-100 justify-start items-center gap-2 visible:flex hidden:hidden" %>

<% toolbar_classes = "
h-14 p-2 bg-white border-b border-gray-100
justify-start items-center gap-2
visible:flex hidden:hidden
rounded-t-lg
" %>
<div role="search">
<div class="<%= toolbar_classes %>" data-<%= stimulus_id %>-target="searchToolbar">
<%= form_with(
Expand All @@ -21,29 +28,16 @@
"data-turbo-frame": table_frame_id,
"data-turbo-action": "replace",
"data-#{stimulus_id}-target": "searchForm",
"data-action": "reset->#{stimulus_id}#search",
"data-action": "input->#{stimulus_id}#search change->#{stimulus_id}#search",
},
) do |form| %>
<label class="items-center gap-1 p-0 inline-flex w-full justify-start relative">
<%= render component("ui/icon").new(name: 'search-line', class: "w-[1.4em] h-[1.4em] fill-gray-500 absolute ml-3") %>
<input
name="q[<%= @search_key %>]"
value="<%= params.dig(:q, @search_key) %>"
type="search"
placeholder="<%= t('.search_placeholder', resources: resource_plural_name) %>"
class="peer w-full placeholder:text-gray-400 py-1.5 px-10 bg-white rounded border border-gray-300 search-cancel:appearance-none"
data-<%= stimulus_id %>-target="searchField"
data-action="<%= stimulus_id %>#search"
aria-label="<%= t('.search_placeholder', resources: resource_plural_name) %>"
>
<button
class="absolute right-0 mr-3 peer-placeholder-shown:hidden"
data-action="<%= stimulus_id %>#clearSearch"
aria-label="<%= t('.clear') %>"
>
<%= render component("ui/icon").new(name: 'close-circle-fill', class: "w-[1.4em] h-[1.4em] fill-gray-500") %>
</button>
</label>
<%= render component('ui/forms/search_field').new(
name: "#{@search_param}[#{@search_key}]",
value: params.dig(@search_param, @search_key),
placeholder: t('.search_placeholder', resources: resource_plural_name),
"data-#{stimulus_id}-target": "searchField",
"aria-label": t('.search_placeholder', resources: resource_plural_name)
) %>
<% end %>

<div class="ml-4">
Expand All @@ -57,18 +51,8 @@

<% if @filters.any? %>
<div class="<%= toolbar_classes %>" data-<%= stimulus_id %>-target="filterToolbar">
<div class="font-semibold text-gray-700 text-sm px-2"><%= t('.refine_search') %>:</div>
<% @filters.each do |filter| %>
<label class="flex gap-2 px-2">
<%= render component('ui/forms/checkbox').new(
name: filter[:name],
value: filter[:value],
size: :s,
form: search_form_id,
'data-action': "#{stimulus_id}#search",
) %>
<span class="text-gray-700 leading-none text-sm self-center"><%= filter[:label] %></span>
</label>
<% @filters.each_with_index do |filter, index| %>
<%= render_ransack_filter_dropdown(filter, index) %>
<% end %>
</div>
<% end %>
Expand Down Expand Up @@ -131,7 +115,10 @@

<tbody class="bg-white text-3.5 line-[150%] text-black">
<% @rows.each do |row| %>
<tr class="<%= row_class_for(row) %>">
<tr class="
border-b border-gray-100 last:border-0
<%= 'bg-gray-15 text-gray-700' if @fade_row_proc&.call(row) %>
">
<% @columns.each do |column| %>
<%= render_data_cell(column.data, row) %>
<% end %>
Expand All @@ -142,7 +129,7 @@
<tr>
<td
colspan="<%= @columns.size %>"
class="text-center py-4 text-3.5 line-[150%] text-black bg-white"
class="text-center py-4 text-3.5 line-[150%] text-black bg-white rounded-b-lg"
>
<%= t('.no_resources_found', resources: resource_plural_name) %>
</td>
Expand All @@ -153,7 +140,7 @@
<% if @prev_page_link || @next_page_link %>
<tfoot>
<tr>
<td colspan="<%= @columns.size %>" class="py-4 bg-white">
<td colspan="<%= @columns.size %>" class="py-4 bg-white rounded-b-lg border-t border-gray-100">
<div class="flex justify-center">
<%= render component('ui/table/pagination').new(
prev_link: @prev_page_link,
Expand Down
5 changes: 0 additions & 5 deletions admin/app/components/solidus_admin/ui/table/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,6 @@ export default class extends Controller {
this.searchFormTarget.requestSubmit()
}

clearSearch() {
this.searchFieldTarget.value = ''
this.search()
}

cancelSearch() {
this.clearSearch()

Expand Down
48 changes: 32 additions & 16 deletions admin/app/components/solidus_admin/ui/table/component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ class SolidusAdmin::UI::Table::Component < SolidusAdmin::BaseComponent
# @param model_class [ActiveModel::Translation] The model class used for translations.
# @param rows [Array] The collection of objects that will be passed to columns for display.
# @param fade_row_proc [Proc, nil] A proc determining if a row should have a faded appearance.
# @param search_param [Symbol] The param for searching.
# @param search_key [Symbol] The key for searching.
# @param search_url [String] The base URL for searching.
#
Expand All @@ -19,20 +20,22 @@ class SolidusAdmin::UI::Table::Component < SolidusAdmin::BaseComponent
# @option batch_actions [String] :action The batch action path.
# @option batch_actions [String] :method The batch action HTTP method for the provided path.
#
#
# @param filters [Array<Hash>] The array of filter definitions.
# @option filters [String] :name The filter's name.
# @option filters [Any] :value The filter's value.
# @option filters [String] :label The filter's label.
# @param filters [Array<Hash>] The list of filter configurations to render.
# @option filters [String] :presentation The display name of the filter dropdown.
# @option filters [String] :combinator The combining logic of the filter dropdown.
# @option filters [String] :attribute The database attribute this filter modifies.
# @option filters [String] :predicate The predicate used for this filter (e.g., "eq" for equals).
# @option filters [Array<Array>] :options An array of arrays, each containing two elements:
# 1. A human-readable presentation of the filter option (e.g., "Active").
# 2. The actual value used for filtering (e.g., "active").
#
# @param prev_page_link [String, nil] The link to the previous page.
# @param next_page_link [String, nil] The link to the next page.
def initialize(
id:,
model_class:,
rows:,
search_key:,
search_url:,
search_key:, search_url:, search_param: :q,
fade_row_proc: nil,
columns: [],
batch_actions: [],
Expand All @@ -47,6 +50,7 @@ def initialize(
@model_class = model_class
@rows = rows
@fade_row_proc = fade_row_proc
@search_param = search_param
@search_key = search_key
@search_url = search_url
@prev_page_link = prev_page_link
Expand Down Expand Up @@ -112,6 +116,19 @@ def render_batch_action_button(batch_action)
)
end

def render_ransack_filter_dropdown(filter, index)
render component("ui/table/ransack_filter").new(
presentation: filter.presentation,
search_param: @search_param,
combinator: filter.combinator,
attribute: filter.attribute,
predicate: filter.predicate,
options: filter.options,
form: search_form_id,
index: index,
)
end

def render_header_cell(cell, **attrs)
cell = cell.call if cell.respond_to?(:call)
cell = @model_class.human_attribute_name(cell) if cell.is_a?(Symbol)
Expand All @@ -133,18 +150,17 @@ def render_data_cell(cell, data)
cell = data.public_send(cell) if cell.is_a?(Symbol)
cell = cell.render_in(self) if cell.respond_to?(:render_in)

content_tag(:td, content_tag(:div, cell, class: "flex items-center gap-1.5"), class: "py-2 px-4 h-10 vertical-align-middle leading-none")
end

def row_class_for(row)
classes = ['border-b', 'border-gray-100']
classes << ['bg-gray-15', 'text-gray-700'] if @fade_row_proc&.call(row)

classes.join(' ')
tag.td(
tag.div(cell, class: "flex items-center gap-1.5"),
class: "
py-2 px-4 h-10 vertical-align-middle leading-none
[tr:last-child_&:first-child]:rounded-bl-lg [tr:last-child_&:last-child]:rounded-br-lg
",
)
end

Column = Struct.new(:header, :data, :class_name, keyword_init: true)
BatchAction = Struct.new(:display_name, :icon, :action, :method, keyword_init: true) # rubocop:disable Lint/StructNewOverride
Filter = Struct.new(:name, :value, :label, keyword_init: true)
Filter = Struct.new(:presentation, :combinator, :attribute, :predicate, :options, keyword_init: true)
private_constant :Column, :BatchAction, :Filter
end
Loading