Skip to content

Commit

Permalink
elia+rainer/admin/stock-edit: [wip]
Browse files Browse the repository at this point in the history
  • Loading branch information
elia committed Dec 6, 2023
1 parent c4115d7 commit de92124
Show file tree
Hide file tree
Showing 25 changed files with 444 additions and 54 deletions.
19 changes: 5 additions & 14 deletions admin/app/components/solidus_admin/orders/cart/component.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,11 @@
<% @order.line_items.each do |line_item| %>
<tr class="border-gray-100 border-t">
<td class="px-6 py-4">
<div class="flex gap-2 grow">
<% variant = line_item.variant %>
<%= render component("ui/thumbnail").new(
src: (variant.images.first || variant.product.gallery.images.first)&.url(:small),
alt: variant.name
) %>
<div class="flex-col">
<div class="leading-5 text-black body-small-bold"><%= variant.name %></div>
<div class="leading-5 text-gray-500 body-small">
SKU: <%= variant.sku %>
<%= variant.options_text.presence&.prepend("- ") %>
</div>
</div>
</div>
<%= render component("ui/resource_item").new(
thumbnail: (line_item.variant.images.first || line_item.variant.product.gallery.images.first)&.url(:small),
title: line_item.variant.name,
subtitle: "#{line_item.variant.sku}#{line_item.variant.options_text.presence&.prepend("- ")}",
) %>
</td>
<td class="px-6 py-4">
<%= form_for(line_item, url: solidus_admin.order_line_item_path(@order, line_item), html: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<div
data-controller="<%= stimulus_id %>"
data-<%= stimulus_id %>-initial-count-on-hand-value="<%= @stock_item.count_on_hand_was || @stock_item.count_on_hand %>"
data-action="input-><%= stimulus_id %>#updateCountOnHand"
>
<%= render component("ui/modal").new(title: t(".title"), close_path: solidus_admin.stock_items_path(page: params[:page], q: params[:q])) do |modal| %>
<%= form_for @stock_item, url: solidus_admin.stock_item_path(@stock_item), html: { id: form_id } do |f| %>
<div class="flex flex-col gap-6 pb-4">
<div class="flex gap-4">
<%= link_to spree.edit_admin_product_variant_path(
@stock_item.variant.product,
@stock_item.variant,
), class: 'hover:bg-gray-25 rounded p-1 w-1/2 border border-gray-100' do %>
<%= render component("ui/resource_item").new(
thumbnail:
(
@stock_item.variant.images.first ||
@stock_item.variant.product.gallery.images.first
)&.url(:small),
title: @stock_item.variant.name,
subtitle:
"#{@stock_item.variant.sku}#{@stock_item.variant.options_text.presence&.prepend(" - ")}",
) %>
<% end %>
<%= link_to spree.edit_admin_stock_location_path(@stock_item.stock_location), class: 'hover:bg-gray-25 rounded p-1 w-1/2 border border-gray-100' do %>
<%= render component("ui/resource_item").new(
title: @stock_item.stock_location.name,
subtitle: "#{Spree::StockLocation.model_name.human} #{@stock_item.stock_location.code}",
) %>
<% end %>
</div>

<%= render component("ui/forms/field").text_field(
f,
:count_on_hand,
disabled: true,
value: @stock_item.count_on_hand_was || @stock_item.count_on_hand,
"data-#{stimulus_id}-target": 'countOnHand',
) %>
<%= render component("ui/forms/field").new(
label: t(".quantity_adjustment"),
hint: t(".quantity_adjustment_hint_html"),
) do %>
<%= render component("ui/forms/input").new(
value: params[:quantity_adjustment] || 0,
name: :quantity_adjustment,
type: :number,
step: 1,
"data-#{stimulus_id}-target": 'quantityAdjustment',
) %>
<% end %>
<%= render component("ui/forms/switch_field").new(
name: "#{f.object_name}[backorderable]",
label: Spree::StockItem.human_attribute_name(:backorderable),
error: f.object.errors[:backorderable],
hint: t(".backorderable_hint_html"),
checked: f.object.backorderable?,
include_hidden: true,
) %>
</div>
<% end %>
<% modal.with_actions do %>
<%= render component("ui/button").new(
tag: :a,
scheme: :secondary,
text: t(".cancel"),
href: solidus_admin.stock_items_path(page: params[:page], q: params[:q]),
) %>
<%= render component("ui/button").new(
tag: :button,
text: t(".submit"),
form: form_id,
) %>
<% end %>
<% end %>
<%= render component("stock_items/index").new(page: @page) %>
</div>
17 changes: 17 additions & 0 deletions admin/app/components/solidus_admin/stock_items/edit/component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Controller } from '@hotwired/stimulus'

export default class extends Controller {
static values = {
initialCountOnHand: Number,
}

static targets = ['countOnHand', 'quantityAdjustment']

connect() {
this.updateCountOnHand()
}

updateCountOnHand() {
this.countOnHandTarget.value = parseInt(this.initialCountOnHandValue) + parseInt(this.quantityAdjustmentTarget.value)
}
}
18 changes: 18 additions & 0 deletions admin/app/components/solidus_admin/stock_items/edit/component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true

class SolidusAdmin::StockItems::Edit::Component < SolidusAdmin::BaseComponent
def initialize(stock_item:, page:)
@stock_item = stock_item
@page = page
end

def title
[
"#{Spree::StockLocation.model_name.human}: #{@stock_item.stock_location.name}",
].join(' / ')
end

def form_id
"#{stimulus_id}-#{dom_id(@stock_item)}"
end
end
10 changes: 10 additions & 0 deletions admin/app/components/solidus_admin/stock_items/edit/component.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
en:
submit: "Save"
cancel: "Cancel"
title: "Edit Stock Levels"
quantity_adjustment: "Quantity Adjustment"
quantity_adjustment_hint_html: |
Enter a positive number to increase the stock level, or a negative number to decrease the stock level.
backorderable_hint_html: |
Enable to allow customers to place orders even when the product is out of stock.<br>
When ordering the product customers will know that the product will be delivered at a later date, once it becomes available again.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
next: next_page_path,
columns: columns,
batch_actions: batch_actions,
url: -> { solidus_admin.edit_stock_item_path(_1, page: params[:page], q: params[:q]) },
},
search: {
name: :q,
Expand Down
31 changes: 27 additions & 4 deletions admin/app/components/solidus_admin/stock_items/index/component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,14 @@ def columns
stock_location_column,
back_orderable_column,
count_on_hand_column,
stock_movements_column,
]
end

def image_column
{
col: { class: "w-[72px]" },
header: tag.span('aria-label': t('.image'), role: 'text'),
header: tag.span('aria-label': Spree::Image.model_name.human, role: 'text'),
data: ->(stock_item) do
image = stock_item.variant.gallery.images.first or return

Expand Down Expand Up @@ -127,8 +128,29 @@ def variant_column
def stock_location_column
{
header: :stock_location,
data: ->(stock_item) do
link_to stock_item.stock_location.name, spree.admin_stock_location_stock_movements_path(stock_item.stock_location.id, q: { variant_sku_eq: stock_item.variant.sku })
data: ->(stock_item) { stock_item.stock_location.name },
}
end

# Cache the stock movement counts to avoid N+1 queries
def stock_movement_counts
@stock_movement_counts ||= Spree::StockMovement.where(stock_item_id: @page.records.select(:id)).group(:stock_item_id).count
end

def stock_movements_column
{
header: :stock_movements,
data: -> do
count = stock_movement_counts[_1.id] || 0

link_to(
"#{count} #{Spree::StockMovement.model_name.human(count: count).downcase}",
spree.admin_stock_location_stock_movements_path(
_1.stock_location.id,
q: { variant_sku_eq: _1.variant.sku },
),
class: 'body-link'
)
end
}
end
Expand All @@ -146,7 +168,8 @@ def count_on_hand_column
{
header: :count_on_hand,
data: ->(stock_item) do
content_tag :div, stock_item.count_on_hand
content_tag :div,
stock_item.count_on_hand
end
}
end
Expand Down
21 changes: 21 additions & 0 deletions admin/app/components/solidus_admin/ui/forms/field/component.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# frozen_string_literal: true

class SolidusAdmin::UI::Forms::Field::Component < SolidusAdmin::BaseComponent
extend SolidusAdmin::ComponentsHelper

def initialize(label:, hint: nil, tip: nil, error: nil, input_attributes: nil, **attributes)
@label = label
@hint = hint
Expand Down Expand Up @@ -70,6 +72,25 @@ def self.text_area(form, method, object: nil, hint: nil, tip: nil, size: :m, **a
)
end

def self.toggle(form, method, object: nil, hint: nil, tip: nil, size: :m, **attributes)
object_name, object, label, errors = extract_form_details(form, object, method)

new(
label: label,
hint: hint,
tip: tip,
error: errors,
).with_content(
component('ui/forms/switch').new(
name: "#{object_name}[#{method}]",
size: size,
checked: object.public_send(method),
include_hidden: true,
**attributes,
)
)
end

def self.extract_form_details(form, object, method)
if form.is_a?(String)
object_name = form
Expand Down
46 changes: 25 additions & 21 deletions admin/app/components/solidus_admin/ui/forms/switch/component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,38 @@ class SolidusAdmin::UI::Forms::Switch::Component < SolidusAdmin::BaseComponent
m: 'w-10 h-6 after:w-5 after:h-5 after:checked:translate-x-4',
}.freeze

def initialize(size: :m, **attributes)
def initialize(size: :m, include_hidden: false, **attributes)
@size = size
@attributes = attributes
end
@include_hidden = include_hidden
@attributes[:class] = "
#{SIZES.fetch(@size)}
rounded-full after:rounded-full
appearance-none inline-block relative p-0.5 cursor-pointer
def call
tag.input(
type: 'checkbox',
class: "
#{SIZES.fetch(@size)}
rounded-full after:rounded-full
appearance-none inline-block relative p-0.5 cursor-pointer
outline-none
focus:ring focus:ring-gray-300 focus:ring-0.5 focus:ring-offset-1
active:ring active:ring-gray-300 active:ring-0.5 active:ring-offset-1
outline-none
focus:ring focus:ring-gray-300 focus:ring-0.5 focus:ring-offset-1
active:ring active:ring-gray-300 active:ring-0.5 active:ring-offset-1
disabled:cursor-not-allowed
after:top-0 after:left-0
after:content-[''] after:block
after:transition-all after:duration-300 after:ease-in-out
disabled:cursor-not-allowed
after:top-0 after:left-0
after:content-[''] after:block
after:transition-all after:duration-300 after:ease-in-out
bg-gray-200 after:bg-white
hover:bg-gray-300
checked:bg-gray-500 checked:hover:bg-gray-70
disabled:opacity-40
#{attributes[:class]}
"
end

bg-gray-200 after:bg-white
hover:bg-gray-300
checked:bg-gray-500 checked:hover:bg-gray-70
disabled:opacity-40
",
def call
input = tag.input(
type: 'checkbox',
**@attributes,
)

@include_hidden ? hidden_field_tag(@attributes.fetch(:name), false) + input : input
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<label class="flex flex-wrap items-center gap-2 w-full border border-gray-100 rounded px-4 py-2">
<div class="flex gap-1 items-center grow py-2">
<span class="text-gray-700 body-tiny-bold body-text-bold"><%= @label %></span>

<%= render component("ui/toggletip").new(text: @tip) if @tip.present? %>
</div>

<%= render component("ui/forms/switch").new(**@attributes) %>
<% if @hint.present? || @error.present? %>
<div
class="
w-full body-small [:disabled~&]:text-gray-300 text-gray-500 flex gap-1 flex-col pt-2 pb-4
"
>
<%= tag.span @hint if @hint.present? %>
<%= tag.span safe_join(@error, tag.br), class: "text-red-400" if @error.present? %>
</div>
<% end %>
</label>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# frozen_string_literal: true

class SolidusAdmin::UI::Forms::SwitchField::Component < SolidusAdmin::BaseComponent
def initialize(label:, error:, hint:, **attributes)
@label = label
@error = error
@hint = hint
@attributes = attributes
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<div class="flex gap-2 grow">
<%= render component("ui/thumbnail").new(
src: @thumbnail,
alt: @title,
) if @thumbnail %>
<div class="flex-col">
<div class="leading-5 text-black body-small-bold"><%= @title %></div>
<div class="leading-5 text-gray-500 body-small"><%= @subtitle %></div>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# frozen_string_literal: true

class SolidusAdmin::UI::ResourceItem::Component < SolidusAdmin::BaseComponent
def initialize(title:, subtitle:, thumbnail: nil)
@thumbnail = thumbnail
@title = title
@subtitle = subtitle
end
end
Loading

0 comments on commit de92124

Please sign in to comment.