From 06203f97b39325646093a96da24b26e225735aa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Wed, 19 Aug 2026 10:43:59 +0000 Subject: [PATCH] wrap the document, record and send-mail editors in real forms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit typing into a field and pressing enter did nothing in the document editor, the customer/item/payment/expense dialogs and the send-document dialog — inputs sat in a div, so the button was the only way to submit. now each wraps in a
, with every non-submit button (cancel, close, add/remove line, "same as billing") marked type="button" so it doesn't submit instead of doing its own thing. the record editors share one dialog shell (EditorShell), so a single form there covers customer, item, payment and expense at once — kept the dialog's grid gap intact with display:contents rather than nesting a box. left the list-view filter boxes and the reports date-range picker alone: none of them has a submit action, so there's nothing for enter to do. Co-Authored-By: Claude Opus 5 --- web/DocumentEditor.tsx | 15 +++--- web/RecordEditors.tsx | 38 +++++++++------ web/SendDocumentDialog.tsx | 97 ++++++++++++++++++++------------------ 3 files changed, 85 insertions(+), 65 deletions(-) diff --git a/web/DocumentEditor.tsx b/web/DocumentEditor.tsx index a417728..a86ff6c 100644 --- a/web/DocumentEditor.tsx +++ b/web/DocumentEditor.tsx @@ -197,7 +197,8 @@ export const DocumentEditor = ({ resource, id, onClose }: Props) => { template !== '' && lines.some((l) => l.name.trim()); - const save = () => { + const save = (ev?: React.FormEvent) => { + ev?.preventDefault(); // The three resources agree on everything below the header, and disagree on nothing but it. const header = isRecurring ? { @@ -260,18 +261,18 @@ export const DocumentEditor = ({ resource, id, onClose }: Props) => { const busy = create.isPending || update.isPending; return ( -
+
{isNew ? `New ${KIND_NOUN[resource]}` : `Edit ${number || KIND_NOUN[resource]}`}
- - -
@@ -517,6 +518,7 @@ export const DocumentEditor = ({ resource, id, onClose }: Props) => {
- + ); }; diff --git a/web/RecordEditors.tsx b/web/RecordEditors.tsx index 792eb1e..1d48e10 100644 --- a/web/RecordEditors.tsx +++ b/web/RecordEditors.tsx @@ -87,21 +87,31 @@ const EditorShell = ({ }) => ( !open && onClose()}> - - {title} - {description && {description}} - + {/* `contents` keeps the form invisible to DialogContent's grid layout — it still owns the gap between + header, body and footer, the form just carries Enter-to-submit across all three. */} +
{ + ev.preventDefault(); + onSave(); + }} + className="contents" + > + + {title} + {description && {description}} + -
{children}
+
{children}
- - - - + + + + +
); @@ -289,7 +299,7 @@ const AddressFields = ({
{label}
{onCopy && ( - )} diff --git a/web/SendDocumentDialog.tsx b/web/SendDocumentDialog.tsx index 15d2be6..546135d 100644 --- a/web/SendDocumentDialog.tsx +++ b/web/SendDocumentDialog.tsx @@ -85,62 +85,69 @@ export const SendDocumentDialog = ({ const missingRecipient = !to.trim(); - const submit = () => { + const submit = (ev?: React.FormEvent) => { + ev?.preventDefault(); action.mutate({ id: target.id, action: 'send', payload: { subject, body, from, to } }, { onSuccess: onClose }); }; return ( !open && onClose()}> - - - {target.resend ? 'Resend' : 'Send'} {kind} {target.number} - - - This emails {target.customerName} through InvoiceShelf. It cannot be undone. - - + {/* `contents` keeps the form invisible to DialogContent's grid layout — it still owns the gap between + header, body and footer, the form just carries Enter-to-submit across all three. */} +
+ + + {target.resend ? 'Resend' : 'Send'} {kind} {target.number} + + + This emails {target.customerName} through InvoiceShelf. It cannot be undone. + + -
-
-
- - setTo(e.target.value)} className="h-8 text-xs" type="email" /> +
+
+
+ + setTo(e.target.value)} className="h-8 text-xs" type="email" /> +
+
+ + setFrom(e.target.value)} className="h-8 text-xs" type="email" /> +
- - setFrom(e.target.value)} className="h-8 text-xs" type="email" /> + + setSubject(e.target.value)} className="h-8 text-xs" />
-
-
- - setSubject(e.target.value)} className="h-8 text-xs" /> -
-
- -