fix(11): address code-review findings in catalog inline-edit
- WR-01: remove blur-commit on EditableCell toggle (onChange already saves+closes; prevents double updateServiceField) - WR-02: skip server action when cell value unchanged (commit + new commitOnBlur dirty-check) - WR-03: on blur, revert required-empty field via cancel() instead of leaving the cell stuck in error state - WR-04: normalize it-IT price input (1.234,50) before parse; Number() rejects trailing garbage - IN-01: render row error in its own <tr> instead of an invalid 7th <td> in a 6-column row Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -34,6 +34,7 @@ function ServiceRow({ service }: { service: ServiceWithTags }) {
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<tr
|
||||
className={`border-b border-[#e5e7eb] hover:bg-[#f9f9f9] transition-colors duration-150 ${
|
||||
!service.active ? "opacity-50" : ""
|
||||
@@ -85,12 +86,15 @@ function ServiceRow({ service }: { service: ServiceWithTags }) {
|
||||
onSave={(v) => saveField("active", v)}
|
||||
/>
|
||||
</td>
|
||||
{error && (
|
||||
</tr>
|
||||
{error && (
|
||||
<tr>
|
||||
<td className="py-1 px-3 text-xs text-red-600" colSpan={6}>
|
||||
{error}
|
||||
</td>
|
||||
)}
|
||||
</tr>
|
||||
</tr>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,12 @@ export function EditableCell({
|
||||
}
|
||||
|
||||
function commit() {
|
||||
// WR-02: skip redundant server action when nothing changed
|
||||
if (tempValue === String(value)) {
|
||||
setError(null);
|
||||
setIsEditing(false);
|
||||
return;
|
||||
}
|
||||
if (required && tempValue.trim().length === 0) {
|
||||
setError("Campo richiesto");
|
||||
return;
|
||||
@@ -55,6 +61,23 @@ export function EditableCell({
|
||||
setIsEditing(false);
|
||||
}
|
||||
|
||||
// Blur path: never leave the cell stuck. No-op if unchanged (WR-02);
|
||||
// revert to the last valid value instead of persisting/holding an
|
||||
// invalid required field while focus has already moved away (WR-03).
|
||||
function commitOnBlur() {
|
||||
if (tempValue === String(value)) {
|
||||
cancel();
|
||||
return;
|
||||
}
|
||||
if (required && tempValue.trim().length === 0) {
|
||||
cancel();
|
||||
return;
|
||||
}
|
||||
setError(null);
|
||||
onSave(tempValue);
|
||||
setIsEditing(false);
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
setTempValue(String(value));
|
||||
setError(null);
|
||||
@@ -103,7 +126,7 @@ export function EditableCell({
|
||||
ref={inputRef as React.Ref<HTMLTextAreaElement>}
|
||||
value={tempValue}
|
||||
onChange={(e) => setTempValue(e.target.value)}
|
||||
onBlur={commit}
|
||||
onBlur={commitOnBlur}
|
||||
onKeyDown={handleKeyDown}
|
||||
placeholder={placeholder}
|
||||
className={cn("ring-1 ring-primary resize-none text-sm", error && "ring-2 ring-red-500")}
|
||||
@@ -120,8 +143,9 @@ export function EditableCell({
|
||||
onSave(next);
|
||||
setIsEditing(false);
|
||||
}}
|
||||
onBlur={commit}
|
||||
onKeyDown={handleKeyDown}
|
||||
// WR-01: onChange already saves + closes — a blur-commit here would
|
||||
// fire a second (possibly stale) updateServiceField for the same field.
|
||||
onKeyDown={(e) => { if (e.key === "Escape") cancel(); }}
|
||||
className="h-4 w-4 cursor-pointer accent-[#1A463C]"
|
||||
/>
|
||||
) : (
|
||||
@@ -130,7 +154,7 @@ export function EditableCell({
|
||||
type={type}
|
||||
value={tempValue}
|
||||
onChange={(e) => setTempValue(e.target.value)}
|
||||
onBlur={commit}
|
||||
onBlur={commitOnBlur}
|
||||
onKeyDown={handleKeyDown}
|
||||
placeholder={placeholder}
|
||||
step={type === "number" ? "0.01" : undefined}
|
||||
|
||||
Reference in New Issue
Block a user