diff --git a/src/components/finance/InvoiceCreateModal.jsx b/src/components/finance/InvoiceCreateModal.jsx
index 9461021..44ecf91 100644
--- a/src/components/finance/InvoiceCreateModal.jsx
+++ b/src/components/finance/InvoiceCreateModal.jsx
@@ -1,9 +1,7 @@
import { useEffect, useMemo, useRef, useState } from 'react'
-import { FaPlus, FaTrashCan, FaXmark } from 'react-icons/fa6'
-import { AutocompleteInput, Button } from '../ui'
+import { AutocompleteInput, Button, Dialog, Field } from '../ui'
import AddressWarning from './AddressWarning'
import { useCustomers } from '../../hooks/useCustomers'
-import { useDialogFocus } from '../../hooks/useDialogFocus'
import { useScrollIntoView } from '../../hooks/useScrollIntoView'
import { createInvoice } from '../../lib/financeApi'
import { canInvoice, euro } from '../../lib/financeFormat'
@@ -65,10 +63,8 @@ function InvoiceForm({ onClose, onCreated, fixedCustomer, source, onShowOverview
const [busy, setBusy] = useState(null) // 'draft' | 'send'
const lockRef = useRef(false)
const aliveRef = useRef(true)
- const dialogRef = useRef(null)
const errorRef = useRef(null)
- const onOverlayKeyDown = useDialogFocus(dialogRef)
// Der Dialog kann höher sein als das Fenster: eine Fehlermeldung oben soll nicht außerhalb des Bildes stehen
useScrollIntoView(errorRef, error)
@@ -79,14 +75,12 @@ function InvoiceForm({ onClose, onCreated, fixedCustomer, source, onShowOverview
}
}, [])
- useEffect(() => {
- const onKey = (e) => {
- // Escape schließt auch die Kundenliste (Autovervollständigung): dann nicht den ganzen Dialog
- if (e.key === 'Escape' && !e.defaultPrevented && !lockRef.current) onClose()
- }
- document.addEventListener('keydown', onKey)
- return () => document.removeEventListener('keydown', onKey)
- }, [onClose])
+ // × und Esc kommen vom Dialog (er ignoriert Esc, wenn die Kundenliste die Taste schon behandelt hat, und solange `busy` gilt).
+ // Die Sperre deckt den Augenblick zwischen Klick und erstem Zeichnen von `busy` ab.
+ const close = () => {
+ if (lockRef.current) return
+ onClose()
+ }
const options = useMemo(() => customerPickerOptions(source?.customers), [source?.customers])
const resolved = fixedCustomer
@@ -144,203 +138,183 @@ function InvoiceForm({ onClose, onCreated, fixedCustomer, source, onShowOverview
const disabled = busy !== null
- return (
-
-
-
+
+
+
+ {busy === 'send' ? 'Sende …' : 'Senden'}
+
)
+
+ // Enter sendet nie (das wäre eine Mail an den Kunden): das Formular hat keinen Absenden-Knopf, jeder Knopf darin ist type="button"
+ return (
+
+ )
}
diff --git a/src/styles/global.css b/src/styles/global.css
index c6c82d0..609bfda 100644
--- a/src/styles/global.css
+++ b/src/styles/global.css
@@ -814,43 +814,29 @@ button.link-danger { background: none; border: 0; padding: 0; font: inherit; fon
.fin-tx-action { grid-area: action; align-self: start; }
}
-/* Dialoge: Rechnung schreiben, Buchung zuordnen. Overlay, Box und Schliessknopf kommen aus KOMPAT (.overlay:has(> .overlay-content), */
-/* .overlay-content, .overlay-close); hier nur, was dort fehlt. */
-.fin-close:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; border-radius: 4px; }
-.fin-close:disabled { opacity: 0.4; cursor: not-allowed; }
-.fin-dialog { max-width: 760px; }
-.fin-dialog:focus { outline: none; }
-.fin-dialog-error {
- margin-bottom: var(--sp-4); padding: var(--sp-3) var(--sp-4); border-radius: var(--r-sm);
- border: 1px solid var(--danger); background: var(--red-bg); color: var(--red-fg); overflow-wrap: anywhere;
-}
-.fin-dialog-warn { margin: 6px 0 0; font-size: 13px; color: var(--orange-fg); overflow-wrap: anywhere; }
-.fin-dialog-sub { margin: 6px 0 0; font-size: 13px; color: var(--text-muted); overflow-wrap: anywhere; }
-.fin-fixed-customer { margin: 0; overflow-wrap: anywhere; }
+/* Dialoge: Rechnung schreiben, Buchung zuordnen. Huelle, Kopf und Schliessknopf kommen vom Baustein Dialog. */
+.fin-fixed-customer { overflow-wrap: anywhere; }
.fin-address-warn { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px; overflow-wrap: anywhere; }
.fin-address-warn-page { margin: 0 0 16px; }
.fin-address-warn-dialog { margin: 0; }
-.fin-items { border: 0; padding: 0; margin: 0 0 var(--sp-4); min-width: 0; }
-.fin-items legend { padding: 0; }
-.fin-item {
- display: grid; grid-template-columns: minmax(0, 1fr) 90px 130px 120px 38px; gap: 8px; align-items: end; margin-bottom: 8px;
-}
+/* Rechnung schreiben: Positionen, Summe, Zahlungsziel und Notiz */
+.fin-items { border: 0; padding: 0; margin: 0; min-width: 0; }
+.fin-items legend { padding: 0; margin-bottom: 4px; font-weight: 500; }
+.fin-items .msg { margin-top: 8px; overflow-wrap: anywhere; }
+.fin-item { display: grid; grid-template-columns: minmax(0, 1fr) 80px 120px 110px auto; gap: 8px 12px; align-items: end; }
+.fin-item + .fin-item { margin-top: 8px; }
.fin-item-field { min-width: 0; }
-.fin-item-label { display: block; margin-bottom: 4px; font-size: 12px; color: var(--text-muted); }
-.fin-item ~ .fin-item .fin-item-label { position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
-.fin-item .form-control.fin-amount { text-align: right; }
-.fin-item-total { display: block; padding: 10px 0; font-weight: 600; }
-.fin-icon-btn {
- width: 38px; height: 40px; display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
- background: transparent; border: 1px solid var(--border-strong); border-radius: 6px; color: var(--text-muted);
-}
-.fin-icon-btn:hover:not(:disabled) { color: var(--red-fg); border-color: var(--red-fg); }
-.fin-icon-btn:disabled { opacity: 0.35; cursor: not-allowed; }
-.fin-icon-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
-.fin-items-foot { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--sp-3); margin-top: var(--sp-3); }
-.fin-total { display: flex; align-items: baseline; gap: var(--sp-4); font-size: 18px; font-weight: 700; }
-.fin-dialog-row { display: grid; grid-template-columns: 180px minmax(0, 1fr); gap: var(--sp-4); }
-.fin-dialog-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--sp-3); margin-top: var(--sp-4); }
+.fin-item .input { width: 100%; }
+.fin-item .fin-amount { text-align: right; }
+.fin-item-label { display: block; margin-bottom: 4px; font-size: 12px; color: var(--text-2); }
+.fin-item + .fin-item .fin-item-label { position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
+.fin-item-sum { text-align: right; }
+.fin-item-total { display: block; line-height: 32px; font-weight: 600; }
+.fin-item-remove { display: flex; align-items: center; height: 32px; }
+.fin-item-remove button:disabled { cursor: not-allowed; }
+.fin-items-foot { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; margin-top: 8px; }
+.fin-total { display: flex; align-items: baseline; gap: 16px; font-size: 18px; font-weight: 600; }
+.fin-dialog-row { display: grid; grid-template-columns: 180px minmax(0, 1fr); gap: 12px; }
/* Zuordnen (Dialog): Fuss, Fakten, Auswahlzeilen, Bestaetigung. Huelle, Kopf und Schliessknopf kommen vom Baustein Dialog. */
.fin-foot { flex: 1; display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px; }
.kv .fin-tx-facts-amount { text-align: left; font-weight: 600; }
@@ -869,17 +855,15 @@ button.link-danger { background: none; border: 0; padding: 0; font: inherit; fon
.fin-confirm p { margin: 0; }
@media (max-width: 860px) {
.fin-foot .btn { flex: 1 1 auto; }
-}
-@media (max-width: 640px) {
- .fin-item { grid-template-columns: minmax(0, 0.7fr) minmax(0, 1fr) minmax(0, 1fr) 38px; grid-template-areas: "desc desc desc desc" "qty price sum remove"; }
+ .fin-item { grid-template-columns: minmax(0, 0.7fr) minmax(0, 1fr) minmax(0, 1fr) auto; grid-template-areas: "desc desc desc desc" "qty price sum remove"; }
+ .fin-item + .fin-item { margin-top: 16px; }
.fin-item-desc { grid-area: desc; }
.fin-item-qty { grid-area: qty; }
.fin-item-price { grid-area: price; }
- .fin-item-sum { grid-area: sum; text-align: right; }
+ .fin-item-sum { grid-area: sum; }
.fin-item-remove { grid-area: remove; }
- .fin-item ~ .fin-item .fin-item-label { position: static; width: auto; height: auto; margin: 0 0 4px; overflow: visible; clip: auto; white-space: normal; }
+ .fin-item + .fin-item .fin-item-label { position: static; width: auto; height: auto; margin: 0 0 4px; overflow: visible; clip: auto; white-space: normal; }
.fin-dialog-row { grid-template-columns: minmax(0, 1fr); }
- .fin-dialog-actions .ui-btn { flex: 1 1 auto; }
}
/* Finanzen: Einstellungen (Formular, Bankzugang, Fixkosten) */