add review submission flow (phase 3)
All checks were successful
deploy / deploy (push) Successful in 1m10s
All checks were successful
deploy / deploy (push) Successful in 1m10s
- Add ReviewNotifier for verification emails - Add review token generation and verification - Add request_review_verification function - Update reviews_section component with write-a-review form - Create ReviewForm LiveView for submitting/editing reviews - Add /reviews/new and /reviews/:id/edit routes - Add review buttons to order detail page - Update block_types to load real review data - Tests for token and verification functions Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,7 @@ defmodule BerrypodWeb.Shop.Pages.OrderDetail do
|
||||
import Phoenix.Component, only: [assign: 3]
|
||||
import Phoenix.LiveView, only: [push_navigate: 2]
|
||||
|
||||
alias Berrypod.{Orders, Pages}
|
||||
alias Berrypod.{Orders, Pages, Reviews}
|
||||
alias BerrypodWeb.R
|
||||
alias Berrypod.Products
|
||||
alias Berrypod.Products.ProductImage
|
||||
@@ -44,7 +44,16 @@ defmodule BerrypodWeb.Shop.Pages.OrderDetail do
|
||||
|
||||
slug = if variant.product.visible, do: variant.product.slug, else: nil
|
||||
|
||||
{id, %{thumb: thumb, slug: slug}}
|
||||
# Check if user has reviewed this product
|
||||
existing_review = Reviews.get_review_by_email_and_product(email, variant.product_id)
|
||||
|
||||
{id,
|
||||
%{
|
||||
thumb: thumb,
|
||||
slug: slug,
|
||||
product_id: variant.product_id,
|
||||
existing_review: existing_review
|
||||
}}
|
||||
end)
|
||||
|
||||
socket =
|
||||
|
||||
@@ -6,7 +6,7 @@ defmodule BerrypodWeb.Shop.Pages.Product do
|
||||
import Phoenix.Component, only: [assign: 2, assign: 3]
|
||||
import Phoenix.LiveView, only: [connected?: 1, push_navigate: 2]
|
||||
|
||||
alias Berrypod.{Analytics, Cart, Pages}
|
||||
alias Berrypod.{Analytics, Cart, Pages, Reviews}
|
||||
alias BerrypodWeb.R
|
||||
alias Berrypod.Images.Optimizer
|
||||
alias Berrypod.Products
|
||||
@@ -69,6 +69,12 @@ defmodule BerrypodWeb.Shop.Pages.Product do
|
||||
|> assign(:variants, variants)
|
||||
|> assign(:page, page)
|
||||
|> assign(:product_discontinued, is_discontinued)
|
||||
|> assign(:review_form, nil)
|
||||
|> assign(:review_status, nil)
|
||||
|> assign(:existing_review, nil)
|
||||
|
||||
# Check if user has an existing review for this product
|
||||
socket = load_existing_review(socket)
|
||||
|
||||
# Block data loaders (related_products, reviews) run after product is assigned
|
||||
extra = Pages.load_block_data(page.blocks, socket.assigns)
|
||||
@@ -126,8 +132,56 @@ defmodule BerrypodWeb.Shop.Pages.Product do
|
||||
end
|
||||
end
|
||||
|
||||
# ── Review events ──────────────────────────────────────────────────────
|
||||
|
||||
def handle_event("request_review", %{"email" => email}, socket) do
|
||||
product = socket.assigns.product
|
||||
|
||||
case Reviews.request_review_verification(email, product.id, product.title) do
|
||||
{:ok, :sent} ->
|
||||
{:noreply,
|
||||
assign(
|
||||
socket,
|
||||
:review_status,
|
||||
{:info, "Check your email for a link to leave your review."}
|
||||
)}
|
||||
|
||||
{:error, :no_purchase} ->
|
||||
{:noreply,
|
||||
assign(
|
||||
socket,
|
||||
:review_status,
|
||||
{:error, "We couldn't find a matching order for this product."}
|
||||
)}
|
||||
|
||||
{:error, :already_reviewed} ->
|
||||
{:noreply,
|
||||
assign(socket, :review_status, {:error, "You've already reviewed this product."})}
|
||||
|
||||
{:error, _reason} ->
|
||||
{:noreply,
|
||||
assign(socket, :review_status, {:error, "Something went wrong. Please try again later."})}
|
||||
end
|
||||
end
|
||||
|
||||
def handle_event(_event, _params, _socket), do: :cont
|
||||
|
||||
# ── Review helpers ───────────────────────────────────────────────────
|
||||
|
||||
defp load_existing_review(socket) do
|
||||
email = socket.assigns[:email_session]
|
||||
product = socket.assigns.product
|
||||
|
||||
if email do
|
||||
case Reviews.get_review_by_email_and_product(email, product.id) do
|
||||
nil -> socket
|
||||
review -> assign(socket, :existing_review, review)
|
||||
end
|
||||
else
|
||||
socket
|
||||
end
|
||||
end
|
||||
|
||||
# ── Variant selection logic ──────────────────────────────────────────
|
||||
|
||||
defp apply_variant_params(params, socket) do
|
||||
|
||||
Reference in New Issue
Block a user