diff --git a/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsx b/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsx index 6700371be..dbede0eb4 100644 --- a/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsx +++ b/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsx @@ -241,7 +241,9 @@ function ProductTitleEditor({ onKeyDown={(event) => { if (event.key === "Enter" && !event.nativeEvent.isComposing) { event.preventDefault(); - runAsynchronouslyWithAlert(save()); + if (draftValue !== storedDisplayName && !isSaving) { + runAsynchronouslyWithAlert(save()); + } } if (event.key === "Escape") { cancelEditing(); @@ -352,6 +354,12 @@ function ProductHeader({ productId, product, productLineName }: ProductHeaderPro icon: , onClick: () => router.push(`/projects/${projectId}/payments/product-lines#product-${productId}`), }, + { + id: "edit", + label: "Edit full details", + icon: , + onClick: () => router.push(`/projects/${projectId}/payments/products/${productId}/edit`), + }, ]} /> @@ -690,6 +698,14 @@ function ProductDetailsSection({ productId, product, config }: ProductDetailsSec setFreeTrialPopoverOpen(false); }; + const handleFreeTrialPopoverOpenChange = (open: boolean) => { + setFreeTrialPopoverOpen(open); + if (open) { + setFreeTrialCount(localFreeTrial ? localFreeTrial[0] : 7); + setFreeTrialUnit(localFreeTrial ? localFreeTrial[1] : 'day'); + } + }; + // ===== PRICES HANDLERS (for deferred save) ===== const handlePricesChange = (newPrices: Product['prices']) => { // Clear the "needs at least one price" error as soon as the user adds one back. @@ -785,7 +801,7 @@ function ProductDetailsSection({ productId, product, config }: ProductDetailsSec name: "Free Trial", tooltip: "Free trial period before billing starts. Customers won't be charged during this period.", open: freeTrialPopoverOpen, - onOpenChange: setFreeTrialPopoverOpen, + onOpenChange: handleFreeTrialPopoverOpenChange, triggerContent: localFreeTrialDisplayText, popoverContent: (
@@ -795,7 +811,7 @@ function ProductDetailsSection({ productId, product, config }: ProductDetailsSec type="number" min={1} value={freeTrialCount} - onChange={(e) => setFreeTrialCount(parseInt(e.target.value) || 1)} + onChange={(e) => setFreeTrialCount(Math.max(1, parseInt(e.target.value) || 1))} /> { if (event.key === "Enter" && !event.nativeEvent.isComposing) { event.preventDefault(); - runAsynchronouslyWithAlert(save()); + if (draftValue !== item.value && !isSaving) { + runAsynchronouslyWithAlert(save()); + } } if (event.key === "Escape") { cancelEditing(); @@ -598,10 +600,10 @@ function ItemValue({ } case "custom-button": { return ( - + ); } case "custom": { @@ -673,6 +674,17 @@ function SaveBar({ onDiscard: () => void, onSave: () => Promise, }) { + const [isSaving, setIsSaving] = useState(false); + + const handleSave = async () => { + setIsSaving(true); + try { + await onSave(); + } finally { + setIsSaving(false); + } + }; + return (
Discard - + Save changes
@@ -734,7 +752,7 @@ export function DesignEditableGrid({ "grid items-center text-sm", resolvedSize.gapX, resolvedSize.gapY, - columns === 2 && "lg:[&>*:nth-child(4n+3)]:pl-4", + columns === 2 && "lg:[&>div:nth-child(even)>:first-child]:pl-4", gridCols, className, )}