Vertrag-UI: Sticky-Header + Sticky-Footer
ContractDetail: der obere Balken (Vertragsnummer, Kunde-Link, Aktions-Buttons wie Folgevertrag/Bearbeiten/Löschen) bleibt beim Scrollen sichtbar. -mx-8 px-8 überbrückt das Layout-Padding, damit der Balken bündig sitzt. ContractForm: Heading + Kunde-Zeile sticky oben, Speichern/ Abbrechen sticky unten. Damit muss man beim langen Formular nicht mehr nach oben oder unten rollen. ScrollToTopButton: von bottom-6 auf bottom-24 verschoben, sonst sitzt der runde Nach-oben-Button beim Runterscrollen genau über dem Speichern-Button im Sticky-Footer. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -29,10 +29,14 @@ export default function ScrollToTopButton() {
|
|||||||
|
|
||||||
if (!isVisible) return null;
|
if (!isVisible) return null;
|
||||||
|
|
||||||
|
// bottom-24 statt bottom-6: der Nach-oben-Button muss auf Seiten mit
|
||||||
|
// Sticky-Footer (z.B. ContractForm Speichern/Abbrechen) über dem Footer
|
||||||
|
// sitzen, sonst hängt er beim Runterscrollen genau über dem Speichern-
|
||||||
|
// Button. 96px reichen für alle heute vorhandenen Sticky-Footer.
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
onClick={scrollToTop}
|
onClick={scrollToTop}
|
||||||
className="fixed bottom-6 right-6 p-3 bg-gray-200 hover:bg-gray-300 text-gray-600 rounded-full shadow-md transition-all duration-300 opacity-70 hover:opacity-100 z-50"
|
className="fixed bottom-24 right-6 p-3 bg-gray-200 hover:bg-gray-300 text-gray-600 rounded-full shadow-md transition-all duration-300 opacity-70 hover:opacity-100 z-50"
|
||||||
aria-label="Nach oben scrollen"
|
aria-label="Nach oben scrollen"
|
||||||
title="Nach oben"
|
title="Nach oben"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1795,7 +1795,10 @@ export default function ContractDetail() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center justify-between mb-6">
|
{/* Sticky-Header: Vertragsnummer + Kunde + Aktionsbuttons bleiben
|
||||||
|
beim Scrollen oben stehen. -mx-8 px-8 zieht den Balken auf die
|
||||||
|
Layout-Padding-Kante, damit's optisch bündig ist. */}
|
||||||
|
<div className="sticky top-0 z-20 -mx-8 px-8 py-4 mb-6 bg-gray-100 border-b border-gray-200 flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center gap-4 mb-2">
|
<div className="flex items-center gap-4 mb-2">
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -777,44 +777,49 @@ export default function ContractForm() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center gap-4 mb-2">
|
{/* Sticky-Header: Zurück-Button + Titel + Kunde-Zeile bleiben beim
|
||||||
<Button variant="ghost" size="sm" onClick={() => navigate(back.to, { state: back.state })}>
|
Scrollen oben stehen. Layout-Padding wird per -mx-8 px-8
|
||||||
<ArrowLeft className="w-4 h-4" />
|
überbrückt, damit der Balken bündig ist. */}
|
||||||
</Button>
|
<div className="sticky top-0 z-20 -mx-8 px-8 pt-4 pb-3 mb-6 bg-gray-100 border-b border-gray-200">
|
||||||
<h1 className="text-2xl font-bold">
|
<div className="flex items-center gap-4 mb-2">
|
||||||
{isEdit ? 'Vertrag bearbeiten' : 'Neuer Vertrag'}
|
<Button variant="ghost" size="sm" onClick={() => navigate(back.to, { state: back.state })}>
|
||||||
</h1>
|
<ArrowLeft className="w-4 h-4" />
|
||||||
|
</Button>
|
||||||
|
<h1 className="text-2xl font-bold">
|
||||||
|
{isEdit ? 'Vertrag bearbeiten' : 'Neuer Vertrag'}
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
{customer && (
|
||||||
|
<p className="text-gray-500 ml-12 flex items-center gap-1">
|
||||||
|
Kunde:{' '}
|
||||||
|
<Link
|
||||||
|
to={`/customers/${customer.id}`}
|
||||||
|
className="text-blue-600 hover:underline"
|
||||||
|
>
|
||||||
|
{customer.companyName || `${customer.firstName} ${customer.lastName}`}
|
||||||
|
</Link>
|
||||||
|
<a
|
||||||
|
href={`/customers/${customer.id}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
title="Kundenakte in neuem Tab öffnen"
|
||||||
|
aria-label="Kundenakte in neuem Tab öffnen"
|
||||||
|
className="text-gray-400 hover:text-blue-600 p-1 rounded"
|
||||||
|
>
|
||||||
|
<ExternalLink className="w-4 h-4" />
|
||||||
|
</a>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setShowCustomerInfo(true)}
|
||||||
|
title="Wichtige Kundendaten anzeigen (Schnellansicht mit Copy-Buttons)"
|
||||||
|
aria-label="Kundendaten anzeigen"
|
||||||
|
className="text-gray-400 hover:text-blue-600 p-1 rounded"
|
||||||
|
>
|
||||||
|
<Info className="w-4 h-4" />
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{customer && (
|
|
||||||
<p className="text-gray-500 ml-12 mb-6 flex items-center gap-1">
|
|
||||||
Kunde:{' '}
|
|
||||||
<Link
|
|
||||||
to={`/customers/${customer.id}`}
|
|
||||||
className="text-blue-600 hover:underline"
|
|
||||||
>
|
|
||||||
{customer.companyName || `${customer.firstName} ${customer.lastName}`}
|
|
||||||
</Link>
|
|
||||||
<a
|
|
||||||
href={`/customers/${customer.id}`}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
title="Kundenakte in neuem Tab öffnen"
|
|
||||||
aria-label="Kundenakte in neuem Tab öffnen"
|
|
||||||
className="text-gray-400 hover:text-blue-600 p-1 rounded"
|
|
||||||
>
|
|
||||||
<ExternalLink className="w-4 h-4" />
|
|
||||||
</a>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => setShowCustomerInfo(true)}
|
|
||||||
title="Wichtige Kundendaten anzeigen (Schnellansicht mit Copy-Buttons)"
|
|
||||||
aria-label="Kundendaten anzeigen"
|
|
||||||
className="text-gray-400 hover:text-blue-600 p-1 rounded"
|
|
||||||
>
|
|
||||||
<Info className="w-4 h-4" />
|
|
||||||
</button>
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{error && (
|
{error && (
|
||||||
<div className="mb-4 p-4 bg-red-50 border border-red-200 text-red-700 rounded-lg">
|
<div className="mb-4 p-4 bg-red-50 border border-red-200 text-red-700 rounded-lg">
|
||||||
@@ -1784,7 +1789,11 @@ export default function ContractForm() {
|
|||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<div className="flex justify-end gap-4">
|
{/* Sticky-Footer: Speichern/Abbrechen bleiben unten sichtbar,
|
||||||
|
damit man beim langen Formular nicht ganz nach unten scrollen
|
||||||
|
muss. -mx-8 px-8 -mb-8 zieht den Balken bündig an die
|
||||||
|
Layout-Padding-Kante. */}
|
||||||
|
<div className="sticky bottom-0 z-20 -mx-8 px-8 pt-3 pb-3 -mb-8 mt-6 bg-gray-100 border-t border-gray-200 flex justify-end gap-4">
|
||||||
<Button type="button" variant="secondary" onClick={() => navigate(back.to, { state: back.state })}>
|
<Button type="button" variant="secondary" onClick={() => navigate(back.to, { state: back.state })}>
|
||||||
Abbrechen
|
Abbrechen
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
Reference in New Issue
Block a user