add admin products list and detail pages

Read-mostly admin views for synced products: filterable/sortable list
with inline visibility toggle, and detail page with images grid,
variants table, storefront controls form, and provider edit links.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jamey
2026-02-16 08:48:51 +00:00
parent ccc14aa5e1
commit 81e94d0d65
9 changed files with 890 additions and 2 deletions

View File

@@ -225,6 +225,7 @@ defmodule SimpleshopTheme.Products do
defp maybe_filter_on_sale(query, _), do: query
defp maybe_filter_in_stock(query, true), do: where(query, [p], p.in_stock == true)
defp maybe_filter_in_stock(query, false), do: where(query, [p], p.in_stock == false)
defp maybe_filter_in_stock(query, _), do: query
defp apply_sort(query, "price_asc"), do: order_by(query, [p], asc: p.cheapest_price)
@@ -264,6 +265,42 @@ defmodule SimpleshopTheme.Products do
|> Repo.all()
end
@doc """
Returns products for the admin list page with sorting, stock filtering,
and full preloads for display.
## Options
* `:visible` - filter by visibility (boolean)
* `:status` - filter by status (string)
* `:category` - filter by category (string)
* `:provider_connection_id` - filter by provider connection
* `:in_stock` - filter by stock status (boolean)
* `:sort` - sort order (string)
"""
def list_products_admin(opts \\ []) do
Product
|> apply_product_filters(opts)
|> maybe_filter_in_stock(opts[:in_stock])
|> apply_sort(opts[:sort])
|> preload([:provider_connection, images: :image, variants: []])
|> Repo.all()
end
@doc """
Returns distinct category names from all products (including hidden/draft).
"""
def list_all_categories do
from(p in Product,
where: not is_nil(p.category),
select: p.category,
distinct: true,
order_by: p.category
)
|> Repo.all()
end
@doc """
Gets a single product by ID.
"""
@@ -310,6 +347,22 @@ defmodule SimpleshopTheme.Products do
|> Repo.update()
end
@doc """
Updates storefront-only fields (visibility and category).
"""
def update_storefront(%Product{} = product, attrs) do
product
|> Product.storefront_changeset(attrs)
|> Repo.update()
end
@doc """
Toggles a product's visibility.
"""
def toggle_visibility(%Product{} = product) do
update_storefront(product, %{visible: !product.visible})
end
@doc """
Deletes a product.
"""

View File

@@ -67,6 +67,14 @@ defmodule SimpleshopTheme.Products.Product do
|> unique_constraint([:provider_connection_id, :provider_product_id])
end
@doc """
Changeset for admin storefront controls (visibility and category only).
"""
def storefront_changeset(product, attrs) do
product
|> cast(attrs, [:visible, :category])
end
@doc """
Changeset for recomputing denormalized fields from variants.
"""