Apply review feedback to editable-grid and product page

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
armaan 2026-07-16 17:19:19 +00:00
parent fc2f817af9
commit c3786b81be
2 changed files with 45 additions and 11 deletions

View File

@ -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: <FolderOpenIcon className="h-4 w-4" />,
onClick: () => router.push(`/projects/${projectId}/payments/product-lines#product-${productId}`),
},
{
id: "edit",
label: "Edit full details",
icon: <PencilSimpleIcon className="h-4 w-4" />,
onClick: () => router.push(`/projects/${projectId}/payments/products/${productId}/edit`),
},
]}
/>
</div>
@ -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: (
<div className="space-y-3">
@ -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))}
/>
<DesignSelectorDropdown
className="w-28"

View File

@ -296,7 +296,9 @@ function EditableTextField({
onKeyDown={(event) => {
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 (
<button
type="button"
<DesignButton
variant="plain"
disabled={item.disabled}
onClick={() => runAsynchronouslyWithAlert(item.onClick())}
onClick={() => item.onClick()}
className={cn(
"flex w-full items-center justify-start gap-2 text-left font-normal",
size.controlHeight,
@ -609,11 +611,10 @@ function ItemValue({
size.controlText,
designEditableGridControlClassName,
editMode && "border-black/[0.18] dark:border-white/[0.22]",
item.disabled && "cursor-not-allowed opacity-50",
)}
>
<span className="min-w-0 truncate">{item.children}</span>
</button>
</DesignButton>
);
}
case "custom": {
@ -673,6 +674,17 @@ function SaveBar({
onDiscard: () => void,
onSave: () => Promise<void>,
}) {
const [isSaving, setIsSaving] = useState(false);
const handleSave = async () => {
setIsSaving(true);
try {
await onSave();
} finally {
setIsSaving(false);
}
};
return (
<div
aria-live="polite"
@ -685,13 +697,19 @@ function SaveBar({
<div className="flex items-center justify-end gap-1.5">
<DesignButton
className="h-8 rounded-lg px-3 text-xs text-muted-foreground"
disabled={isSaving}
onClick={onDiscard}
size="sm"
variant="ghost"
>
Discard
</DesignButton>
<DesignButton className="h-8 rounded-lg px-3 text-xs" onClick={onSave} size="sm">
<DesignButton
className="h-8 rounded-lg px-3 text-xs"
loading={isSaving}
onClick={handleSave}
size="sm"
>
Save changes
</DesignButton>
</div>
@ -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,
)}