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,
)}