diff --git a/frontend/src/pages/AdministrationPage.tsx b/frontend/src/pages/AdministrationPage.tsx index cf9fce2..9631994 100644 --- a/frontend/src/pages/AdministrationPage.tsx +++ b/frontend/src/pages/AdministrationPage.tsx @@ -1,9 +1,10 @@ -import { useEffect, useMemo, useState } from "react"; +import { useEffect, useMemo, useState, type KeyboardEvent as ReactKeyboardEvent } from "react"; import { useLocation } from "react-router-dom"; import { apiGet, apiPost } from "../lib/api"; import type { AdministrationOverview, MedicationCategory, PathogenKind } from "../lib/types"; type DatasetKey = "farmers" | "medications" | "pathogens" | "antibiotics"; +type CatalogDatasetKey = Exclude; type EditableRow = { id: string; @@ -74,6 +75,373 @@ function isFarmerRowIncomplete(row: EditableRow) { }); } +function mapFarmerMutation(row: EditableRow) { + return { + id: row.id || null, + customerNumber: row.customerNumber || null, + companyName: row.companyName, + contactPerson: row.contactPerson || null, + street: row.street || null, + houseNumber: row.houseNumber || null, + postalCode: row.postalCode || null, + city: row.city || null, + email: row.email || null, + phoneNumber: row.phoneNumber || null, + active: row.active, + }; +} + +function medicationCategoryLabel(category: MedicationCategory | undefined) { + switch (category) { + case "IN_UDDER": + return "ins Euter"; + case "SYSTEMIC_ANTIBIOTIC": + return "systemisch Antibiotika"; + case "SYSTEMIC_PAIN": + return "systemisch Schmerzmittel"; + case "DRY_SEALER": + return "Versiegler"; + case "DRY_ANTIBIOTIC": + return "Trockenstellerprobe Antibiotika"; + default: + return "-"; + } +} + +function pathogenKindLabel(kind: PathogenKind | undefined) { + switch (kind) { + case "BACTERIAL": + return "bakteriell"; + case "NO_GROWTH": + return "kein Wachstum"; + case "CONTAMINATED": + return "verunreinigt"; + case "OTHER": + return "sonstiges"; + default: + return "-"; + } +} + +function catalogDialogTitle(dataset: CatalogDatasetKey) { + switch (dataset) { + case "medications": + return "Medikament bearbeiten"; + case "pathogens": + return "Erreger bearbeiten"; + case "antibiotics": + return "Antibiogramm bearbeiten"; + } +} + +function catalogEmptyMessage(dataset: CatalogDatasetKey) { + switch (dataset) { + case "medications": + return "Noch keine Medikamente vorhanden."; + case "pathogens": + return "Noch keine Erreger vorhanden."; + case "antibiotics": + return "Noch keine Antibiogramm-Eintraege vorhanden."; + } +} + +function isCatalogFieldInvalid( + dataset: CatalogDatasetKey, + row: EditableRow, + field: keyof EditableRow, + showValidation: boolean, +) { + if (!showValidation) { + return false; + } + + switch (field) { + case "name": + return isBlankValue(row.name); + case "category": + return dataset === "medications" && !row.category; + case "kind": + return dataset === "pathogens" && !row.kind; + default: + return false; + } +} + +function isCatalogRowIncomplete(dataset: CatalogDatasetKey, row: EditableRow) { + if (isBlankValue(row.name)) { + return true; + } + if (dataset === "medications") { + return !row.category; + } + if (dataset === "pathogens") { + return !row.kind; + } + return false; +} + +function CatalogForm({ + dataset, + row, + onChange, + showValidation, +}: { + dataset: CatalogDatasetKey; + row: EditableRow; + onChange: (patch: Partial) => void; + showValidation: boolean; +}) { + return ( +
+ {dataset === "medications" ? ( +
+ + + +
+ ) : null} + + {dataset === "pathogens" ? ( +
+ + + + +
+ ) : null} + + {dataset === "antibiotics" ? ( +
+ + + +
+ ) : null} +
+ ); +} + +function FarmerForm({ + row, + onChange, + showValidation, +}: { + row: EditableRow; + onChange: (patch: Partial) => void; + showValidation: boolean; +}) { + return ( +
+
+ + + +
+ +
+ + + + +
+ +
+ + +
+ +
+ +
+
+ ); +} + function normalizeOverview(overview: AdministrationOverview): DatasetsState { return { farmers: overview.farmers.map((entry) => ({ @@ -166,7 +534,14 @@ export default function AdministrationPage() { const [datasets, setDatasets] = useState(null); const [saving, setSaving] = useState(false); const [message, setMessage] = useState(null); - const [showValidation, setShowValidation] = useState(false); + const [newFarmerDraft, setNewFarmerDraft] = useState(null); + const [editingFarmer, setEditingFarmer] = useState(null); + const [showCreateValidation, setShowCreateValidation] = useState(false); + const [showEditValidation, setShowEditValidation] = useState(false); + const [newCatalogDraft, setNewCatalogDraft] = useState(null); + const [editingCatalogRow, setEditingCatalogRow] = useState(null); + const [showCatalogCreateValidation, setShowCatalogCreateValidation] = useState(false); + const [showCatalogEditValidation, setShowCatalogEditValidation] = useState(false); const selectedDataset: DatasetKey = useMemo(() => { if (location.pathname.startsWith("/admin/medikamente")) { @@ -196,94 +571,171 @@ export default function AdministrationPage() { const rows = useMemo(() => datasets?.[selectedDataset] ?? [], [datasets, selectedDataset]); const isFarmerDataset = selectedDataset === "farmers"; + const catalogDataset = selectedDataset === "farmers" ? null : selectedDataset; - function updateRow(index: number, patch: Partial) { - setDatasets((current) => { - if (!current) { - return current; - } - const nextRows = current[selectedDataset].map((row, rowIndex) => - rowIndex === index ? { ...row, ...patch } : row, - ); - return { - ...current, - [selectedDataset]: nextRows, - }; - }); + useEffect(() => { + setNewFarmerDraft(null); + setEditingFarmer(null); + setShowCreateValidation(false); + setShowEditValidation(false); + setNewCatalogDraft(null); + setEditingCatalogRow(null); + setShowCatalogCreateValidation(false); + setShowCatalogEditValidation(false); + }, [selectedDataset]); + + async function saveFarmers(rowsToSave: EditableRow[]) { + const response = await apiPost("/catalog/farmers", rowsToSave.map(mapFarmerMutation)); + setDatasets((current) => (current ? { ...current, farmers: response } : current)); } - function addRow() { - setDatasets((current) => { - if (!current) { - return current; - } - return { - ...current, - [selectedDataset]: [...current[selectedDataset], emptyRow(selectedDataset)], - }; - }); + async function saveCatalogRows(dataset: CatalogDatasetKey, rowsToSave: EditableRow[]) { + let response: EditableRow[]; + + switch (dataset) { + case "medications": + response = await apiPost("/catalog/medications", rowsToSave.map((row) => ({ + id: row.id || null, + name: row.name, + category: row.category, + active: row.active, + }))); + break; + case "pathogens": + response = await apiPost("/catalog/pathogens", rowsToSave.map((row) => ({ + id: row.id || null, + code: row.code || null, + name: row.name, + kind: row.kind, + active: row.active, + }))); + break; + case "antibiotics": + response = await apiPost("/catalog/antibiotics", rowsToSave.map((row) => ({ + id: row.id || null, + code: row.code || null, + name: row.name, + active: row.active, + }))); + break; + } + + setDatasets((current) => (current ? { ...current, [dataset]: response } : current)); } - async function handleSave() { - if (!datasets) { + function handleStartFarmerCreate() { + setNewFarmerDraft(emptyRow("farmers")); + setShowCreateValidation(false); + setMessage(null); + } + + function handleCancelFarmerCreate() { + setNewFarmerDraft(null); + setShowCreateValidation(false); + setMessage(null); + } + + async function handleCreateFarmer() { + if (!newFarmerDraft) { return; } - setShowValidation(true); - if (selectedDataset === "farmers" && rows.some(isFarmerRowIncomplete)) { + + setShowCreateValidation(true); + if (isFarmerRowIncomplete(newFarmerDraft)) { setMessage("Bitte alle Pflichtfelder fuer den Landwirt ausfuellen."); return; } - if (selectedDataset !== "farmers" && rows.some((row) => !row.name?.trim())) { - setMessage("Bitte alle Pflichtfelder ausfuellen."); - return; - } + setSaving(true); setMessage(null); try { - let response: EditableRow[]; - switch (selectedDataset) { - case "farmers": - response = await apiPost("/catalog/farmers", rows.map((row) => ({ - id: row.id || null, - customerNumber: row.customerNumber || null, - companyName: row.companyName, - contactPerson: row.contactPerson || null, - street: row.street || null, - houseNumber: row.houseNumber || null, - postalCode: row.postalCode || null, - city: row.city || null, - email: row.email || null, - phoneNumber: row.phoneNumber || null, - active: row.active, - }))); - break; - case "medications": - response = await apiPost("/catalog/medications", rows.map((row) => ({ - id: row.id || null, - name: row.name, - category: row.category, - active: row.active, - }))); - break; - case "pathogens": - response = await apiPost("/catalog/pathogens", rows.map((row) => ({ - id: row.id || null, - code: row.code || null, - name: row.name, - kind: row.kind, - active: row.active, - }))); - break; - case "antibiotics": - response = await apiPost("/catalog/antibiotics", rows.map((row) => ({ - id: row.id || null, - code: row.code || null, - name: row.name, - active: row.active, - }))); - break; - } - setDatasets((current) => (current ? { ...current, [selectedDataset]: response } : current)); + await saveFarmers([newFarmerDraft]); + setNewFarmerDraft(null); + setShowCreateValidation(false); + setMessage("Landwirt gespeichert."); + } catch (saveError) { + setMessage((saveError as Error).message); + } finally { + setSaving(false); + } + } + + function openFarmerDialog(row: EditableRow) { + setEditingFarmer({ ...row }); + setShowEditValidation(false); + setMessage(null); + } + + function closeFarmerDialog() { + setEditingFarmer(null); + setShowEditValidation(false); + } + + async function handleSaveFarmerEdit() { + if (!editingFarmer) { + return; + } + + setShowEditValidation(true); + if (isFarmerRowIncomplete(editingFarmer)) { + setMessage("Bitte alle Pflichtfelder fuer den Landwirt ausfuellen."); + return; + } + + setSaving(true); + setMessage(null); + try { + await saveFarmers([editingFarmer]); + setEditingFarmer(null); + setShowEditValidation(false); + setMessage("Landwirt gespeichert."); + } catch (saveError) { + setMessage((saveError as Error).message); + } finally { + setSaving(false); + } + } + + function handleFarmerRowKeyDown(event: ReactKeyboardEvent, row: EditableRow) { + if (event.key !== "Enter" && event.key !== " ") { + return; + } + event.preventDefault(); + openFarmerDialog(row); + } + + function handleStartCatalogCreate() { + if (!catalogDataset) { + return; + } + setNewCatalogDraft(emptyRow(catalogDataset)); + setShowCatalogCreateValidation(false); + setMessage(null); + } + + function handleCancelCatalogCreate() { + setNewCatalogDraft(null); + setShowCatalogCreateValidation(false); + setMessage(null); + } + + async function handleCreateCatalogEntry() { + if (!catalogDataset || !newCatalogDraft) { + return; + } + + setShowCatalogCreateValidation(true); + if (isCatalogRowIncomplete(catalogDataset, newCatalogDraft)) { + setMessage("Bitte alle Pflichtfelder ausfuellen."); + return; + } + + setSaving(true); + setMessage(null); + try { + await saveCatalogRows(catalogDataset, [newCatalogDraft]); + setNewCatalogDraft(null); + setShowCatalogCreateValidation(false); setMessage("Aenderungen gespeichert."); } catch (saveError) { setMessage((saveError as Error).message); @@ -292,6 +744,50 @@ export default function AdministrationPage() { } } + function openCatalogDialog(row: EditableRow) { + setEditingCatalogRow({ ...row }); + setShowCatalogEditValidation(false); + setMessage(null); + } + + function closeCatalogDialog() { + setEditingCatalogRow(null); + setShowCatalogEditValidation(false); + } + + async function handleSaveCatalogEdit() { + if (!catalogDataset || !editingCatalogRow) { + return; + } + + setShowCatalogEditValidation(true); + if (isCatalogRowIncomplete(catalogDataset, editingCatalogRow)) { + setMessage("Bitte alle Pflichtfelder ausfuellen."); + return; + } + + setSaving(true); + setMessage(null); + try { + await saveCatalogRows(catalogDataset, [editingCatalogRow]); + setEditingCatalogRow(null); + setShowCatalogEditValidation(false); + setMessage("Aenderungen gespeichert."); + } catch (saveError) { + setMessage((saveError as Error).message); + } finally { + setSaving(false); + } + } + + function handleCatalogRowKeyDown(event: ReactKeyboardEvent, row: EditableRow) { + if (event.key !== "Enter" && event.key !== " ") { + return; + } + event.preventDefault(); + openCatalogDialog(row); + } + return (
@@ -299,8 +795,8 @@ export default function AdministrationPage() {

Verwaltung

{DATASET_TITLES[selectedDataset]}

- Bestehende Datensaetze lassen sich inline aendern. Bei Umbenennungen bleibt der alte - Satz inaktiv sichtbar. + Neue Eintraege werden ueber den Button unter der Tabelle erfasst. Bestehende Eintraege lassen sich per + Klick in der Tabelle bearbeiten.

@@ -314,213 +810,275 @@ export default function AdministrationPage() {
-

Datensatz

{DATASET_LABELS[selectedDataset]}

{isFarmerDataset ? (
- {rows.map((row, index) => ( -
-
- - - -
+
+
+

Zum Bearbeiten eine Zeile anklicken.

+
-
- - - - -
+
+ + + + + + + + + + + {rows.length ? ( + rows.map((row, index) => ( + openFarmerDialog(row)} + onKeyDown={(event) => handleFarmerRowKeyDown(event, row)} + > + + + + + + )) + ) : ( + + + + )} + +
FirmaAnsprechpartnerTelefonE-Mail
{row.companyName || "-"}{row.contactPerson || "-"}{row.phoneNumber || "-"}{row.email || "-"}
+ Noch keine Landwirte vorhanden. +
+
-
- - -
- -
- -
-
- ))} +
+ +
+
) : ( -
- - - - - {selectedDataset === "medications" ? : null} - {selectedDataset === "pathogens" || selectedDataset === "antibiotics" ? : null} - {selectedDataset === "pathogens" ? : null} - - - - - {rows.map((row, index) => ( - - - {selectedDataset === "medications" ? ( - + + )} + +
NameKategorieKuerzelTypAktiv
- updateRow(index, { name: event.target.value })} - /> - - + + + + {selectedDataset === "medications" ? : null} + {selectedDataset === "pathogens" || selectedDataset === "antibiotics" ? : null} + {selectedDataset === "pathogens" ? : null} + + + + + {rows.length ? ( + rows.map((row, index) => ( + openCatalogDialog(row)} + onKeyDown={(event) => handleCatalogRowKeyDown(event, row)} + > + + {selectedDataset === "medications" ? : null} + {selectedDataset === "pathogens" || selectedDataset === "antibiotics" ? ( + + ) : null} + {selectedDataset === "pathogens" ? : null} + + + )) + ) : ( + + - ) : null} - {selectedDataset === "pathogens" || selectedDataset === "antibiotics" ? ( - - ) : null} - {selectedDataset === "pathogens" ? ( - - ) : null} - - - ))} - -
NameKategorieKuerzelTypAktiv
{row.name || "-"}{medicationCategoryLabel(row.category)}{row.code || "-"}{pathogenKindLabel(row.kind)}{row.active ? "sichtbar" : "inaktiv"}
- - - - - - - - updateRow(index, { code: event.target.value })} - /> - - - - -
+ {catalogEmptyMessage(selectedDataset)} +
+
+ +
+ +
+ )} - -
- - -
+ + {newFarmerDraft ? ( +
+
event.stopPropagation()}> +
+ + setNewFarmerDraft((current) => (current ? { ...current, ...patch } : current)) + } + showValidation={showCreateValidation} + /> +
+ +
+ + +
+
+
+ ) : null} + + {editingFarmer ? ( +
+
event.stopPropagation()}> +
+
+

Bearbeitung

+

Landwirt bearbeiten

+
+
+ +
+ + setEditingFarmer((current) => (current ? { ...current, ...patch } : current)) + } + showValidation={showEditValidation} + /> +
+ +
+ + +
+
+
+ ) : null} + + {catalogDataset && newCatalogDraft ? ( +
+
event.stopPropagation()}> +
+ + setNewCatalogDraft((current) => (current ? { ...current, ...patch } : current)) + } + showValidation={showCatalogCreateValidation} + /> +
+ +
+ + +
+
+
+ ) : null} + + {catalogDataset && editingCatalogRow ? ( +
+
event.stopPropagation()}> +
+
+

Bearbeitung

+

{catalogDialogTitle(catalogDataset)}

+
+
+ +
+ + setEditingCatalogRow((current) => (current ? { ...current, ...patch } : current)) + } + showValidation={showCatalogEditValidation} + /> +
+ +
+ + +
+
+
+ ) : null} ); } diff --git a/frontend/src/pages/UserManagementPage.tsx b/frontend/src/pages/UserManagementPage.tsx index b568d80..e384010 100644 --- a/frontend/src/pages/UserManagementPage.tsx +++ b/frontend/src/pages/UserManagementPage.tsx @@ -58,6 +58,29 @@ export default function UserManagementPage() { } }, [user]); + function resetProfileData() { + setProfileData({ + displayName: user?.displayName || "", + companyName: user?.companyName || "", + street: user?.street || "", + houseNumber: user?.houseNumber || "", + postalCode: user?.postalCode || "", + city: user?.city || "", + email: user?.email || "", + phoneNumber: user?.phoneNumber || "", + }); + } + + function openProfileDialog() { + resetProfileData(); + setShowProfileForm(true); + } + + function closeProfileDialog() { + resetProfileData(); + setShowProfileForm(false); + } + useEffect(() => { async function loadUsers() { try { @@ -170,6 +193,14 @@ export default function UserManagementPage() { } } + function closeCreateUserDialog() { + setShowCreateForm(false); + setNewUserName(""); + setNewUserEmail(""); + setNewUserPassword(""); + setNewUserPasswordConfirm(""); + } + async function handleSaveProfile(e: React.FormEvent) { e.preventDefault(); if (!profileData.displayName.trim()) { @@ -269,36 +300,191 @@ export default function UserManagementPage() { {/* Own Profile Form (nur für Hauptbenutzer) */} {isPrimaryUser && !isAdmin && (
- {!showProfileForm ? ( -
+
+
+

Meine Stammdaten

+

{user?.displayName}

+
+ +
+
+ )} + + {/* Tabelle mit Benutzern */} +
+
+
+

{isAdmin ? "Hauptnutzer" : "Unterbenutzer"}

+

{isAdmin ? "Registrierte Hauptnutzer" : "Ihre Unterbenutzer"}

+
+
+ + {loading ? ( +
Benutzer werden geladen...
+ ) : users.length === 0 ? ( +
+ {isAdmin ? "Keine Hauptnutzer vorhanden." : "Keine Unterbenutzer vorhanden."} +
+ ) : ( +
+ + + + + {isAdmin && } + + + + + + + + {users.map((entry) => ( + + + {isAdmin && } + + + + + + ))} + +
NameFirmaE-MailStatusLetzte ÄnderungAktion
+ {entry.displayName} + {(entry as SubUserRow & { companyName?: string }).companyName ?? "-"}{entry.email ?? "-"} + + {entry.active ? "Freigegeben" : "Gesperrt"} + + {formatDate(entry.updatedAt)} + +
+
+ )} + + {isPrimaryUser && !isAdmin ? ( +
+ +
+ ) : null} +
+ + {isPrimaryUser && !isAdmin && showCreateForm ? ( +
+
event.stopPropagation()} onSubmit={handleCreateUser}> +
-

Meine Stammdaten

-

{user?.displayName}

+

Neuer Unterbenutzer

+

Benutzer anlegen

+
+ +
+
+ + + + +
+
+ +
+
- ) : ( - <> -
-
-

Meine Stammdaten

-

Stammdaten bearbeiten

-
- + +
+ ) : null} + + {isPrimaryUser && !isAdmin && showProfileForm ? ( +
+
event.stopPropagation()} onSubmit={handleSaveProfile}> +
+
+

Meine Stammdaten

+

Stammdaten bearbeiten

- +
+ +
+
-
- -
- - - )} - - )} - - {/* Create Sub-user Form (nur für Hauptnutzer) */} - {isPrimaryUser && !isAdmin && ( -
- {!showCreateForm ? ( -
-
-

Neuer Unterbenutzer

-

Benutzer anlegen

+
+ +
+
- ) : ( - <> -
-
-

Neuer Unterbenutzer

-

Benutzer anlegen

-
- -
-
- - - - -
- -
-
- - )} -
- )} - - {/* Tabelle mit Benutzern */} -
-
-
-

{isAdmin ? "Hauptnutzer" : "Unterbenutzer"}

-

{isAdmin ? "Registrierte Hauptnutzer" : "Ihre Unterbenutzer"}

-
+
- - {loading ? ( -
Benutzer werden geladen...
- ) : users.length === 0 ? ( -
- {isAdmin ? "Keine Hauptnutzer vorhanden." : "Keine Unterbenutzer vorhanden."} -
- ) : ( -
- - - - - {isAdmin && } - - - - - - - - {users.map((entry) => ( - - - {isAdmin && } - - - - - - ))} - -
NameFirmaE-MailStatusLetzte ÄnderungAktion
- {entry.displayName} - {(entry as SubUserRow & { companyName?: string }).companyName ?? "-"}{entry.email ?? "-"} - - {entry.active ? "Freigegeben" : "Gesperrt"} - - {formatDate(entry.updatedAt)} - -
-
- )} -
- - {/* Info-Box */} -
-
- Hinweis -

- {isAdmin - ? "Hauptnutzer sind die primären Kontoinhaber. Wenn Sie einen Hauptnutzer sperren, können sich dieser und alle zugehörigen Nebennutzer nicht mehr anmelden. Die Daten bleiben erhalten und können durch Freigabe wieder aktiviert werden." - : "Unterbenutzer können Proben registrieren und bearbeiten, aber keine neuen Benutzer anlegen. Wenn Sie einen Unterbenutzer sperren, kann sich dieser nicht mehr anmelden."} -

-
-
+ ) : null}
); } diff --git a/frontend/src/styles/global.css b/frontend/src/styles/global.css index 9c87fc6..1d5bbd2 100644 --- a/frontend/src/styles/global.css +++ b/frontend/src/styles/global.css @@ -570,11 +570,18 @@ a { overflow: auto; } +.admin-catalog-list, .admin-farmer-list { display: grid; gap: 20px; } +.admin-catalog-section, +.admin-farmer-section { + display: grid; + gap: 16px; +} + .admin-farmer-card { display: grid; gap: 16px; @@ -584,6 +591,15 @@ a { background: rgba(255, 255, 255, 0.42); } +.admin-catalog-form { + display: grid; + gap: 16px; + padding: 24px; + border: 1px solid rgba(37, 49, 58, 0.08); + border-radius: 24px; + background: rgba(255, 255, 255, 0.42); +} + .admin-farmer-card__row { display: grid; gap: 16px; @@ -610,6 +626,11 @@ a { justify-content: center; } +.admin-catalog-form__toggle { + min-width: 140px; + justify-content: center; +} + .data-table { width: 100%; border-collapse: collapse; @@ -630,6 +651,32 @@ a { text-transform: uppercase; } +.admin-farmer-table__row { + cursor: pointer; +} + +.admin-catalog-table__row { + cursor: pointer; +} + +.admin-farmer-table__row:hover, +.admin-farmer-table__row:focus-visible, +.admin-catalog-table__row:hover, +.admin-catalog-table__row:focus-visible { + background: rgba(17, 109, 99, 0.08); + outline: none; +} + +.admin-farmer-table__empty { + color: var(--muted); + text-align: center !important; +} + +.admin-catalog-table__empty { + color: var(--muted); + text-align: center !important; +} + @media (max-width: 1200px) { .admin-farmer-card__row--primary, .admin-farmer-card__row--address, @@ -647,6 +694,7 @@ a { } @media (max-width: 720px) { + .admin-catalog-form, .admin-farmer-card { padding: 18px; } @@ -850,6 +898,14 @@ a { justify-content: flex-end; } +.user-management-page__create-action { + margin-top: 18px; +} + +.user-management-page__profile-header { + margin-bottom: 0; +} + .invoice-template-page { min-height: 0; overflow: hidden; @@ -1520,6 +1576,10 @@ a { justify-content: flex-end; } +.dialog__actions--start { + justify-content: flex-start; +} + .dialog__actions a { display: inline-flex; align-items: center; @@ -1531,6 +1591,11 @@ a { min-height: 0; } +.dialog__body--form, +.dialog__body--farmer { + overflow: auto; +} + .dialog__body--pdf { height: min(80vh, 900px); }