diff --git a/app/components/product/ProductDescription.tsx b/app/components/product/ProductDescription.tsx new file mode 100644 index 0000000..e6c9059 --- /dev/null +++ b/app/components/product/ProductDescription.tsx @@ -0,0 +1,33 @@ +import { AlignRight } from "lucide-react"; + +interface ProductDescriptionProps { + description?: string | null; +} + +/** + * Full product description block. Preserves the seller's line breaks (bullet + * lists from Instagram/WooCommerce imports come in as `\n`-separated lines), + * with a proper heading so it reads as its own section on the detail page. + * + * Renders nothing when no description is set — the section header shouldn't + * appear on products the seller hasn't filled out yet. + */ +export function ProductDescription({ description }: ProductDescriptionProps) { + const body = (description || "").trim(); + if (!body) return null; + + return ( +
+
+ +

توضیحات محصول

+
+
+ {body} +
+
+ ); +} diff --git a/app/components/product/ProductDetailView.tsx b/app/components/product/ProductDetailView.tsx index e0db283..d989ec2 100644 --- a/app/components/product/ProductDetailView.tsx +++ b/app/components/product/ProductDetailView.tsx @@ -3,6 +3,7 @@ import { ProductImageCarousel, ProductActions, ProductInfo, + ProductDescription, ColorSelector, SizeSelector, ContactButton, @@ -138,7 +139,12 @@ export const ProductDetailView = memo(function ProductDetailView({ discountPrice: v?.discountPrice && v.discountPrice !== "0" && - Number(v.discountPrice) !== Number(v.price) + // Only surface as a "discount" when it's a real saving. A value equal + // to (or somehow larger than) `price` is stale/mis-entered state, not a + // discount — sellers occasionally end up with matching numbers when + // they set "no discount" on edit, and rendering a strikethrough on that + // fakes a promo the seller didn't intend. + Number(v.discountPrice) < Number(v.price) ? v.discountPrice : null, }); @@ -713,9 +719,10 @@ export const ProductDetailView = memo(function ProductDetailView({ - {/* Full-width below the fold: contact, reviews, similar */} + {/* Full-width below the fold: description, contact, reviews, similar */} {productActiveStatus && ( <> + {!isOwner && (
diff --git a/app/components/product/ProductInfo.tsx b/app/components/product/ProductInfo.tsx index 061b14c..f031aee 100644 --- a/app/components/product/ProductInfo.tsx +++ b/app/components/product/ProductInfo.tsx @@ -25,6 +25,8 @@ export const ProductInfo = ({ ) : null}

{product.title}

-

{product.description}

+ {/* Description moved into the dedicated ProductDescription section below + the buy actions so multi-line seller copy renders with line breaks and + appears on desktop too. */} ); diff --git a/app/components/product/index.ts b/app/components/product/index.ts index 4d37a0f..1efbde5 100644 --- a/app/components/product/index.ts +++ b/app/components/product/index.ts @@ -2,6 +2,7 @@ export { ProductHeader } from "./ProductHeader"; export { ProductImageCarousel } from "./ProductImageCarousel"; export { ProductActions } from "./ProductActions"; export { ProductInfo } from "./ProductInfo"; +export { ProductDescription } from "./ProductDescription"; export { ColorSelector } from "./ColorSelector"; export { SizeSelector } from "./SizeSelector"; export { ContactButton } from "./ContactButton"; diff --git a/src/api/types/models/index.ts b/src/api/types/models/index.ts index 9835651..4ffe851 100644 --- a/src/api/types/models/index.ts +++ b/src/api/types/models/index.ts @@ -3508,23 +3508,35 @@ export interface ProductAttributeValue { */ export interface ProductCategory { /** - * + * * @type {string} * @memberof ProductCategory */ readonly id?: string; /** - * + * * @type {string} * @memberof ProductCategory */ name: string; /** - * + * * @type {string} * @memberof ProductCategory */ description?: string | null; + /** + * UUID of this category's parent (null for top-level parents). + * @type {string} + * @memberof ProductCategory + */ + readonly parentId?: string | null; + /** + * Human-readable name of the parent (null for top-level parents). + * @type {string} + * @memberof ProductCategory + */ + readonly parentName?: string | null; } /** *