-
Notifications
You must be signed in to change notification settings - Fork 2
Pricefix2 #283
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
Pricefix2 #283
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,12 +1,12 @@ | ||||||||||||||||||||||||||||
| "use client"; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| import { useState, useMemo } from "react"; | ||||||||||||||||||||||||||||
| import { useState } from "react"; | ||||||||||||||||||||||||||||
| import { VariantSelector, type Variant } from "./variant-selector"; | ||||||||||||||||||||||||||||
| import { AddToCartButton } from "./add-to-cart-button"; | ||||||||||||||||||||||||||||
| import { PriceDisplay } from "./price-display"; | ||||||||||||||||||||||||||||
| import { StockBadge } from "./stock-badge"; | ||||||||||||||||||||||||||||
| import { Truck } from "lucide-react"; | ||||||||||||||||||||||||||||
| import { DiscountType, type DiscountInfo } from "@/lib/discount-utils"; | ||||||||||||||||||||||||||||
| import { DiscountType, type DiscountInfo, getEffectiveDiscount } from "@/lib/discount-utils"; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| interface ProductDetailsProps { | ||||||||||||||||||||||||||||
| product: { | ||||||||||||||||||||||||||||
|
|
@@ -41,34 +41,45 @@ export function ProductDetails({ | |||||||||||||||||||||||||||
| storeSlug, | ||||||||||||||||||||||||||||
| onImageChange, | ||||||||||||||||||||||||||||
| }: ProductDetailsProps) { | ||||||||||||||||||||||||||||
| const defaultVariant = useMemo( | ||||||||||||||||||||||||||||
| () => variants.find((v) => v.isDefault) || variants[0] || null, | ||||||||||||||||||||||||||||
| [variants] | ||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| const [selectedVariant, setSelectedVariant] = useState<Variant | null>(defaultVariant); | ||||||||||||||||||||||||||||
| // No variant pre-selected: show product.price initially, variant price only after user selection | ||||||||||||||||||||||||||||
| const [selectedVariant, setSelectedVariant] = useState<Variant | null>(null); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // Calculate current price and inventory based on variant selection | ||||||||||||||||||||||||||||
| // Show product default price until user explicitly picks a variant | ||||||||||||||||||||||||||||
| const currentPrice = selectedVariant?.price ?? product.price; | ||||||||||||||||||||||||||||
| const currentCompareAtPrice = selectedVariant?.compareAtPrice ?? product.compareAtPrice; | ||||||||||||||||||||||||||||
| const currentInventory = selectedVariant?.inventoryQty ?? product.inventoryQty; | ||||||||||||||||||||||||||||
| const currentInventory = selectedVariant ? selectedVariant.inventoryQty : product.inventoryQty; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // Build discount info for price display | ||||||||||||||||||||||||||||
| // Build discount info for product and variant | ||||||||||||||||||||||||||||
| const productDiscount: DiscountInfo = { | ||||||||||||||||||||||||||||
| discountType: product.discountType ?? null, | ||||||||||||||||||||||||||||
| discountValue: product.discountValue ?? null, | ||||||||||||||||||||||||||||
| discountStartDate: product.discountStartDate ? new Date(product.discountStartDate) : null, | ||||||||||||||||||||||||||||
| discountEndDate: product.discountEndDate ? new Date(product.discountEndDate) : null, | ||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| const variantDiscount: DiscountInfo | null = selectedVariant ? { | ||||||||||||||||||||||||||||
| discountType: selectedVariant.discountType ?? null, | ||||||||||||||||||||||||||||
| discountValue: selectedVariant.discountValue ?? null, | ||||||||||||||||||||||||||||
| discountStartDate: selectedVariant.discountStartDate ? new Date(selectedVariant.discountStartDate) : null, | ||||||||||||||||||||||||||||
| discountEndDate: selectedVariant.discountEndDate ? new Date(selectedVariant.discountEndDate) : null, | ||||||||||||||||||||||||||||
| } : null; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // Use variant discount if active, otherwise fall back to product discount | ||||||||||||||||||||||||||||
| const effectiveDiscount = getEffectiveDiscount(productDiscount, variantDiscount); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // Check if product has courier pricing | ||||||||||||||||||||||||||||
| const hasCourierPricing = (product.courierPriceInsideDhaka ?? 0) > 0 || (product.courierPriceOutsideDhaka ?? 0) > 0; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| const handleVariantChange = (variant: Variant) => { | ||||||||||||||||||||||||||||
| setSelectedVariant(variant); | ||||||||||||||||||||||||||||
| // Update image gallery if variant has an image | ||||||||||||||||||||||||||||
| if (variant.image && onImageChange) { | ||||||||||||||||||||||||||||
| onImageChange(variant.image); | ||||||||||||||||||||||||||||
| // Toggle: if already selected, deselect it (go back to default product price) | ||||||||||||||||||||||||||||
| if (selectedVariant?.id === variant.id) { | ||||||||||||||||||||||||||||
| setSelectedVariant(null); | ||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||
| setSelectedVariant(variant); | ||||||||||||||||||||||||||||
| // Update image gallery if variant has an image | ||||||||||||||||||||||||||||
| if (variant.image && onImageChange) { | ||||||||||||||||||||||||||||
| onImageChange(variant.image); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
Comment on lines
+74
to
+82
|
||||||||||||||||||||||||||||
| // Toggle: if already selected, deselect it (go back to default product price) | |
| if (selectedVariant?.id === variant.id) { | |
| setSelectedVariant(null); | |
| } else { | |
| setSelectedVariant(variant); | |
| // Update image gallery if variant has an image | |
| if (variant.image && onImageChange) { | |
| onImageChange(variant.image); | |
| } | |
| setSelectedVariant(variant); | |
| // Update image gallery if variant has an image | |
| if (variant.image && onImageChange) { | |
| onImageChange(variant.image); |
Copilot
AI
Feb 22, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When a variant is deselected (toggled off), the image gallery is not reset to the default product image. The current logic only handles updating the image when a variant is selected, but when setSelectedVariant(null) is called, the image remains on the last selected variant's image rather than reverting to the product's default image.
To fix this, add an else branch that resets the image when deselecting, or call onImageChange with the product's default thumbnail when setting the variant to null.
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,6 +1,5 @@ | ||||||||||||||
| "use client"; | ||||||||||||||
|
|
||||||||||||||
| import { useState } from "react"; | ||||||||||||||
| import { Label } from "@/components/ui/label"; | ||||||||||||||
| import { Button } from "@/components/ui/button"; | ||||||||||||||
| import { cn } from "@/lib/utils"; | ||||||||||||||
|
|
@@ -25,6 +24,7 @@ export interface Variant { | |||||||||||||
|
|
||||||||||||||
| interface VariantSelectorProps { | ||||||||||||||
| variants: Variant[]; | ||||||||||||||
| selectedVariantId?: string | null; | ||||||||||||||
| onVariantChange?: (variant: Variant) => void; | ||||||||||||||
| className?: string; | ||||||||||||||
| } | ||||||||||||||
|
|
@@ -36,14 +36,17 @@ interface VariantSelectorProps { | |||||||||||||
| */ | ||||||||||||||
| export function VariantSelector({ | ||||||||||||||
| variants, | ||||||||||||||
| selectedVariantId, | ||||||||||||||
| onVariantChange, | ||||||||||||||
| className, | ||||||||||||||
| }: VariantSelectorProps) { | ||||||||||||||
| const defaultVariant = variants.find((v) => v.isDefault) || variants[0] || null; | ||||||||||||||
| const [selectedVariant, setSelectedVariant] = useState<Variant | null>(defaultVariant); | ||||||||||||||
| // Use external selectedVariantId if provided, otherwise manage locally | ||||||||||||||
| const selectedVariant = selectedVariantId | ||||||||||||||
| ? variants.find((v) => v.id === selectedVariantId) ?? null | ||||||||||||||
| : null; | ||||||||||||||
|
|
||||||||||||||
| // Early return if no variants | ||||||||||||||
| if (variants.length === 0 || !selectedVariant) { | ||||||||||||||
| if (variants.length === 0) { | ||||||||||||||
| return null; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
|
|
@@ -69,7 +72,6 @@ export function VariantSelector({ | |||||||||||||
| }); | ||||||||||||||
|
|
||||||||||||||
| const handleVariantSelect = (variant: Variant) => { | ||||||||||||||
| setSelectedVariant(variant); | ||||||||||||||
| onVariantChange?.(variant); | ||||||||||||||
| }; | ||||||||||||||
|
|
||||||||||||||
|
|
@@ -80,12 +82,12 @@ export function VariantSelector({ | |||||||||||||
| <div className="space-y-2"> | ||||||||||||||
| <Label> | ||||||||||||||
| Variant:{" "} | ||||||||||||||
| <span className="font-semibold">{selectedVariant.name}</span> | ||||||||||||||
| <span className="font-semibold">{selectedVariant ? selectedVariant.name : "Select a variant"}</span> | ||||||||||||||
|
||||||||||||||
| <span className="font-semibold">{selectedVariant ? selectedVariant.name : "Select a variant"}</span> | |
| <span className="font-semibold"> | |
| {selectedVariant | |
| ? selectedVariant.name | |
| : "Price varies by variant\u00a0-\u00a0please select"} | |
| </span> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When no variant is selected (
selectedVariantis null), the discount calculation falls back toproductDiscount, butcurrentPricealso falls back toproduct.price. This creates a potential issue: if a variant has a different price than the product, the discount might be calculated incorrectly when no variant is selected.For example, if the product has price 100 with 10% discount, but all variants have price 200 with no discount, showing "90" (discounted product price) when no variant is selected could be misleading if users can only purchase variants, not the base product.
Verify that the pricing logic aligns with the business requirements - should the product's base price and discount be shown when variants exist but none is selected?