Feste Fächer-Liste je Objekttyp (Vorlagen-Positionen: Dropdown + Sonstiges)
CI / backend-tests (push) Successful in 1m0s
CI / frontend-build (push) Successful in 5m56s

Nutzer-Vorgabe: Rucksack-Fächer sollen aus einer festen, je Objekttyp
gepflegten Liste gewählt werden können statt jedes Mal Freitext einzutippen.
Neue Tabelle fach (Migration 0004), CRUD-Endpoints /faecher, Admin-Tab
Struktur bekommt Fach-Verwaltung, Vorlagen-Editor nutzt Dropdown mit
"Sonstiges"-Freitext-Fallback.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L85hmKbvX7Cqkq47KnQhFt
This commit is contained in:
2026-09-04 10:24:15 +02:00
co-authored by Claude Sonnet 5
parent 2f11011ed1
commit a2877f5672
11 changed files with 493 additions and 17 deletions
+132
View File
@@ -2263,3 +2263,135 @@ Keine Commits in dieser Session.
- frontend/src/pages/admin/BenutzerSection.tsx | 117 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
---
## 2026-09-04 09:51 09:56 (4m)
**Beschreibung:** Claude Code Session
**Projekt:** backend
### Commits
- 2f11011 Admin-Portal: Bearbeiten für Bereich/Kategorie/Objekttyp/Standort/Benutzer, Vorlagen-Versionierung und Objekt-Duplizieren im Frontend nachgerüstet
### Geänderte Dateien
- DEVLOG.md | 212 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- backend/app/api/v1/endpoints/stammdaten.py | 68 +++++++++++++++++++++++
- backend/app/schemas/stammdaten.py | 22 ++++++++
- backend/tests/test_stammdaten.py | 62 +++++++++++++++++++++
- frontend/src/api/types.ts | 9 ++++
- frontend/src/pages/admin/BenutzerSection.tsx | 92 ++++++++++++++++++++++++++++---
- frontend/src/pages/admin/BereichSection.tsx | 42 ++++++++++++++-
- frontend/src/pages/admin/KategorieSection.tsx | 43 ++++++++++++++-
- frontend/src/pages/admin/ObjektSection.tsx | 79 ++++++++++++++++++++++++---
- frontend/src/pages/admin/ObjekttypSection.tsx | 42 ++++++++++++++-
- frontend/src/pages/admin/StandortSection.tsx | 40 +++++++++++++-
- frontend/src/pages/admin/VorlageSection.tsx | 99 ++++++++++++++++++++++++++++++++--
---
## 2026-09-04 09:57 09:58 (0m)
**Beschreibung:** Claude Code Session
**Projekt:** asb-material
### Commits
Keine Commits in dieser Session.
### Geänderte Dateien
- DEVLOG.md | 212 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- backend/app/api/v1/endpoints/stammdaten.py | 68 +++++++++++++++++++++++
- backend/app/schemas/stammdaten.py | 22 ++++++++
- backend/tests/test_stammdaten.py | 62 +++++++++++++++++++++
- frontend/src/api/types.ts | 9 ++++
- frontend/src/pages/admin/BenutzerSection.tsx | 92 ++++++++++++++++++++++++++++---
- frontend/src/pages/admin/BereichSection.tsx | 42 ++++++++++++++-
- frontend/src/pages/admin/KategorieSection.tsx | 43 ++++++++++++++-
- frontend/src/pages/admin/ObjektSection.tsx | 79 ++++++++++++++++++++++++---
- frontend/src/pages/admin/ObjekttypSection.tsx | 42 ++++++++++++++-
- frontend/src/pages/admin/StandortSection.tsx | 40 +++++++++++++-
- frontend/src/pages/admin/VorlageSection.tsx | 99 ++++++++++++++++++++++++++++++++--
---
## 2026-09-04 10:14 10:14 (0m)
**Beschreibung:** Claude Code Session
**Projekt:** asb-material
### Commits
Keine Commits in dieser Session.
### Geänderte Dateien
- DEVLOG.md | 212 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- backend/app/api/v1/endpoints/stammdaten.py | 68 +++++++++++++++++++++++
- backend/app/schemas/stammdaten.py | 22 ++++++++
- backend/tests/test_stammdaten.py | 62 +++++++++++++++++++++
- frontend/src/api/types.ts | 9 ++++
- frontend/src/pages/admin/BenutzerSection.tsx | 92 ++++++++++++++++++++++++++++---
- frontend/src/pages/admin/BereichSection.tsx | 42 ++++++++++++++-
- frontend/src/pages/admin/KategorieSection.tsx | 43 ++++++++++++++-
- frontend/src/pages/admin/ObjektSection.tsx | 79 ++++++++++++++++++++++++---
- frontend/src/pages/admin/ObjekttypSection.tsx | 42 ++++++++++++++-
- frontend/src/pages/admin/StandortSection.tsx | 40 +++++++++++++-
- frontend/src/pages/admin/VorlageSection.tsx | 99 ++++++++++++++++++++++++++++++++--
---
## 2026-09-04 10:15 10:15 (0m)
**Beschreibung:** Claude Code Session
**Projekt:** asb-material
### Commits
Keine Commits in dieser Session.
### Geänderte Dateien
- DEVLOG.md | 212 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- backend/app/api/v1/endpoints/stammdaten.py | 68 +++++++++++++++++++++++
- backend/app/schemas/stammdaten.py | 22 ++++++++
- backend/tests/test_stammdaten.py | 62 +++++++++++++++++++++
- frontend/src/api/types.ts | 9 ++++
- frontend/src/pages/admin/BenutzerSection.tsx | 92 ++++++++++++++++++++++++++++---
- frontend/src/pages/admin/BereichSection.tsx | 42 ++++++++++++++-
- frontend/src/pages/admin/KategorieSection.tsx | 43 ++++++++++++++-
- frontend/src/pages/admin/ObjektSection.tsx | 79 ++++++++++++++++++++++++---
- frontend/src/pages/admin/ObjekttypSection.tsx | 42 ++++++++++++++-
- frontend/src/pages/admin/StandortSection.tsx | 40 +++++++++++++-
- frontend/src/pages/admin/VorlageSection.tsx | 99 ++++++++++++++++++++++++++++++++--
---
## 2026-09-04 10:15 10:15 (0m)
**Beschreibung:** Claude Code Session
**Projekt:** asb-material
### Commits
Keine Commits in dieser Session.
### Geänderte Dateien
- DEVLOG.md | 212 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- backend/app/api/v1/endpoints/stammdaten.py | 68 +++++++++++++++++++++++
- backend/app/schemas/stammdaten.py | 22 ++++++++
- backend/tests/test_stammdaten.py | 62 +++++++++++++++++++++
- frontend/src/api/types.ts | 9 ++++
- frontend/src/pages/admin/BenutzerSection.tsx | 92 ++++++++++++++++++++++++++++---
- frontend/src/pages/admin/BereichSection.tsx | 42 ++++++++++++++-
- frontend/src/pages/admin/KategorieSection.tsx | 43 ++++++++++++++-
- frontend/src/pages/admin/ObjektSection.tsx | 79 ++++++++++++++++++++++++---
- frontend/src/pages/admin/ObjekttypSection.tsx | 42 ++++++++++++++-
- frontend/src/pages/admin/StandortSection.tsx | 40 +++++++++++++-
- frontend/src/pages/admin/VorlageSection.tsx | 99 ++++++++++++++++++++++++++++++++--
---
## 2026-09-04 10:17 10:17 (0m)
**Beschreibung:** Claude Code Session
**Projekt:** asb-material
### Commits
Keine Commits in dieser Session.
### Geänderte Dateien
- DEVLOG.md | 212 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- backend/app/api/v1/endpoints/stammdaten.py | 68 +++++++++++++++++++++++
- backend/app/schemas/stammdaten.py | 22 ++++++++
- backend/tests/test_stammdaten.py | 62 +++++++++++++++++++++
- frontend/src/api/types.ts | 9 ++++
- frontend/src/pages/admin/BenutzerSection.tsx | 92 ++++++++++++++++++++++++++++---
- frontend/src/pages/admin/BereichSection.tsx | 42 ++++++++++++++-
- frontend/src/pages/admin/KategorieSection.tsx | 43 ++++++++++++++-
- frontend/src/pages/admin/ObjektSection.tsx | 79 ++++++++++++++++++++++++---
- frontend/src/pages/admin/ObjekttypSection.tsx | 42 ++++++++++++++-
- frontend/src/pages/admin/StandortSection.tsx | 40 +++++++++++++-
- frontend/src/pages/admin/VorlageSection.tsx | 99 ++++++++++++++++++++++++++++++++--
---
+36
View File
@@ -0,0 +1,36 @@
"""fach-Tabelle: feste Fächer-Liste je Objekttyp
Revision ID: 0004_add_fach
Revises: 0003_add_benutzer_email
Create Date: 2026-09-04
Nutzer-Vorgabe: feste Fächer-Liste pro Objekttyp statt Freitext bei jeder
Vorlagenposition. Additive Schema-Erweiterung, bestehende Vorlagenpositionen
(freies Fach-Textfeld) bleiben unverändert nutzbar (Prompt-Vorgabe "optional
sonstiges für freies Textfeld").
"""
from typing import Sequence, Union
from alembic import op
revision: str = "0004_add_fach"
down_revision: Union[str, None] = "0003_add_benutzer_email"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
op.execute(
"""
CREATE TABLE fach (
id SERIAL PRIMARY KEY,
objekttyp_id INTEGER NOT NULL REFERENCES objekttyp(id),
name TEXT NOT NULL,
sortierung INTEGER NOT NULL DEFAULT 0
);
"""
)
def downgrade() -> None:
op.execute("DROP TABLE fach;")
+55 -1
View File
@@ -5,11 +5,14 @@ from sqlalchemy.ext.asyncio import AsyncSession
from app.api.deps import get_current_user, require_roles
from app.db.session import get_db
from app.models.auth import RolleTyp
from app.models.stammdaten import Bereich, Kategorie, Material, Objekttyp, Standort
from app.models.stammdaten import Bereich, Fach, Kategorie, Material, Objekttyp, Standort
from app.schemas.stammdaten import (
BereichCreate,
BereichRead,
BereichUpdate,
FachCreate,
FachRead,
FachUpdate,
KategorieCreate,
KategorieRead,
KategorieUpdate,
@@ -179,6 +182,57 @@ async def aendere_objekttyp(
return objekttyp
# -- Fach (feste Fächer-Liste je Objekttyp) ----------------------------------
@router.get("/faecher", response_model=list[FachRead])
async def liste_faecher(
db: AsyncSession = Depends(get_db), _=Depends(get_current_user)
) -> list[Fach]:
result = await db.execute(select(Fach).order_by(Fach.objekttyp_id, Fach.sortierung, Fach.name))
return list(result.scalars().all())
@router.post("/faecher", response_model=FachRead, status_code=status.HTTP_201_CREATED)
async def erstelle_fach(
payload: FachCreate,
db: AsyncSession = Depends(get_db),
_=Depends(require_roles(RolleTyp.administration)),
) -> Fach:
fach = Fach(**payload.model_dump())
db.add(fach)
await db.flush()
return fach
@router.patch("/faecher/{fach_id}", response_model=FachRead)
async def aendere_fach(
fach_id: int,
payload: FachUpdate,
db: AsyncSession = Depends(get_db),
_=Depends(require_roles(RolleTyp.administration)),
) -> Fach:
fach = await db.get(Fach, fach_id)
if fach is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Fach nicht gefunden")
for feld, wert in payload.model_dump(exclude_unset=True).items():
setattr(fach, feld, wert)
await db.flush()
return fach
@router.delete("/faecher/{fach_id}", status_code=status.HTTP_204_NO_CONTENT)
async def loesche_fach(
fach_id: int,
db: AsyncSession = Depends(get_db),
_=Depends(require_roles(RolleTyp.administration)),
) -> None:
fach = await db.get(Fach, fach_id)
if fach is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Fach nicht gefunden")
await db.delete(fach)
await db.flush()
# -- Material (Prompt 07) --------------------------------------------------
@router.get("/materialien", response_model=list[MaterialRead])
+13
View File
@@ -50,6 +50,19 @@ class Objekttyp(Base):
name: Mapped[str] = mapped_column(String, nullable=False)
class Fach(Base):
"""Nutzer-Vorgabe: feste Fächer-Liste je Objekttyp, damit beim Anlegen einer
Vorlagenposition nicht jedes Mal Freitext getippt werden muss. `sortierung`
steuert die Anzeigereihenfolge (z. B. von oben nach unten im Rucksack)."""
__tablename__ = "fach"
id: Mapped[int] = mapped_column(primary_key=True)
objekttyp_id: Mapped[int] = mapped_column(ForeignKey("objekttyp.id"), nullable=False)
name: Mapped[str] = mapped_column(String, nullable=False)
sortierung: Mapped[int] = mapped_column(default=0)
class Material(Base):
__tablename__ = "material"
+16
View File
@@ -67,6 +67,22 @@ class ObjekttypUpdate(BaseModel):
name: str | None = None
class FachCreate(BaseModel):
objekttyp_id: int
name: str
sortierung: int = 0
class FachRead(FachCreate):
model_config = ConfigDict(from_attributes=True)
id: int
class FachUpdate(BaseModel):
name: str | None = None
sortierung: int | None = None
class MaterialCreate(BaseModel):
name: str
artikelnummer: str | None = None
+56
View File
@@ -122,3 +122,59 @@ async def test_admin_can_update_objekttyp(client, admin_user):
)
assert response.status_code == 200
assert response.json()["name"] == "Neu"
@pytest.mark.asyncio
async def test_fach_lifecycle(client, admin_user):
"""Nutzer-Vorgabe: feste Fächer-Liste je Objekttyp."""
token = await login(client, "admin1")
bereich = await client.post("/api/v1/bereiche", json={"name": "B"}, headers=auth_header(token))
objekttyp = await client.post(
"/api/v1/objekttypen",
json={"bereich_id": bereich.json()["id"], "name": "Rucksack"},
headers=auth_header(token),
)
objekttyp_id = objekttyp.json()["id"]
erstellt = await client.post(
"/api/v1/faecher",
json={"objekttyp_id": objekttyp_id, "name": "Fach 1", "sortierung": 1},
headers=auth_header(token),
)
assert erstellt.status_code == 201
fach_id = erstellt.json()["id"]
liste = await client.get("/api/v1/faecher", headers=auth_header(token))
assert any(f["id"] == fach_id for f in liste.json())
patch_response = await client.patch(
f"/api/v1/faecher/{fach_id}", json={"name": "Fach 1 links"}, headers=auth_header(token)
)
assert patch_response.status_code == 200
assert patch_response.json()["name"] == "Fach 1 links"
delete_response = await client.delete(f"/api/v1/faecher/{fach_id}", headers=auth_header(token))
assert delete_response.status_code == 204
liste_nach_delete = await client.get("/api/v1/faecher", headers=auth_header(token))
assert not any(f["id"] == fach_id for f in liste_nach_delete.json())
@pytest.mark.asyncio
async def test_mitarbeiter_cannot_create_fach(client, mitarbeiter_user, admin_user, db_session):
from app.models.stammdaten import Bereich, Objekttyp
bereich = Bereich(name="B2")
db_session.add(bereich)
await db_session.flush()
objekttyp = Objekttyp(bereich_id=bereich.id, name="Rucksack2")
db_session.add(objekttyp)
await db_session.flush()
token = await login(client, "mitarbeiter1")
response = await client.post(
"/api/v1/faecher",
json={"objekttyp_id": objekttyp.id, "name": "Fach X"},
headers=auth_header(token),
)
assert response.status_code == 403
+7
View File
@@ -98,6 +98,13 @@ export interface Objekttyp {
name: string;
}
export interface Fach {
id: number;
objekttyp_id: number;
name: string;
sortierung: number;
}
export interface Me {
id: number;
name: string;
+13 -2
View File
@@ -1,9 +1,10 @@
import { useEffect, useState } from "react";
import { apiRequest } from "../api/client";
import type { Benutzer, Bereich, Beladungsvorlage, Kategorie, Material, Objekt, Objekttyp, Standort } from "../api/types";
import type { Benutzer, Bereich, Beladungsvorlage, Fach, Kategorie, Material, Objekt, Objekttyp, Standort } from "../api/types";
import { BenutzerSection } from "./admin/BenutzerSection";
import { BereichSection } from "./admin/BereichSection";
import { FachSection } from "./admin/FachSection";
import { KategorieSection } from "./admin/KategorieSection";
import { MaterialSection } from "./admin/MaterialSection";
import { ObjektSection } from "./admin/ObjektSection";
@@ -40,12 +41,13 @@ export function AdminPage() {
const [objekte, setObjekte] = useState<Objekt[]>([]);
const [materialien, setMaterialien] = useState<Material[]>([]);
const [benutzer, setBenutzer] = useState<Benutzer[]>([]);
const [faecher, setFaecher] = useState<Fach[]>([]);
const [fehler, setFehler] = useState<string | null>(null);
const [laedt, setLaedt] = useState(true);
const [tab, setTab] = useState<Tab>("objekte");
async function ladeAlles() {
const [be, ka, ot, s, v, o, m, b] = await Promise.all([
const [be, ka, ot, s, v, o, m, b, f] = await Promise.all([
apiRequest<Bereich[]>("/bereiche"),
apiRequest<Kategorie[]>("/kategorien"),
apiRequest<Objekttyp[]>("/objekttypen"),
@@ -54,6 +56,7 @@ export function AdminPage() {
apiRequest<Objekt[]>("/objekte"),
apiRequest<Material[]>("/materialien"),
apiRequest<Benutzer[]>("/benutzer"),
apiRequest<Fach[]>("/faecher"),
]);
setBereiche(be);
setKategorien(ka);
@@ -63,6 +66,7 @@ export function AdminPage() {
setObjekte(o);
setMaterialien(m);
setBenutzer(b);
setFaecher(f);
}
useEffect(() => {
@@ -111,6 +115,7 @@ export function AdminPage() {
vorlagen={vorlagen}
objekttypen={objekttypen}
materialien={materialien}
faecher={faecher}
onGeaendert={ladeAlles}
onFehler={setFehler}
/>
@@ -134,6 +139,12 @@ export function AdminPage() {
onGeaendert={ladeAlles}
onFehler={setFehler}
/>
<FachSection
objekttypen={objekttypen}
faecher={faecher}
onGeaendert={ladeAlles}
onFehler={setFehler}
/>
</>
)}
{tab === "standorte" && (
+54
View File
@@ -0,0 +1,54 @@
import { useState } from "react";
import type { Fach } from "../../api/types";
const SONSTIGES = "__sonstiges__";
interface Props {
faecher: Fach[];
wert: string;
onAendern: (wert: string) => void;
}
/** Dropdown mit fester Fächer-Liste (je Objekttyp) + "Sonstiges" für Freitext,
* damit Vorlagen-Positionen nicht immer per Hand getippt werden müssen. */
export function FachAuswahl({ faecher, wert, onAendern }: Props) {
const istBekanntesFach = faecher.some((f) => f.name === wert);
const [sonstigesAktiv, setSonstigesAktiv] = useState(wert !== "" && !istBekanntesFach);
if (sonstigesAktiv) {
return (
<input
className="input"
style={{ width: "8rem" }}
value={wert}
onChange={(e) => onAendern(e.target.value)}
placeholder="Fach (frei)"
/>
);
}
return (
<select
className="input"
style={{ width: "8rem" }}
value={wert}
onChange={(e) => {
if (e.target.value === SONSTIGES) {
setSonstigesAktiv(true);
onAendern("");
} else {
onAendern(e.target.value);
}
}}
>
<option value="">Fach</option>
{faecher.map((f) => (
<option key={f.id} value={f.name}>
{f.name}
</option>
))}
<option value={SONSTIGES}>Sonstiges</option>
</select>
);
}
+99
View File
@@ -0,0 +1,99 @@
import { useState } from "react";
import { apiRequest } from "../../api/client";
import type { Fach, Objekttyp } from "../../api/types";
interface Props {
objekttypen: Objekttyp[];
faecher: Fach[];
onGeaendert: () => Promise<void>;
onFehler: (text: string) => void;
}
/** Nutzer-Vorgabe: feste Fächer-Liste je Objekttyp (z. B. Rucksack-Fächer),
* damit bei Vorlagen-Positionen aus einer Liste gewählt werden kann statt
* jedes Mal Freitext einzutippen (Sonstiges bleibt dort als Freitext erhalten). */
export function FachSection({ objekttypen, faecher, onGeaendert, onFehler }: Props) {
const [objekttypId, setObjekttypId] = useState<number | "">("");
const [name, setName] = useState("");
const [wirdAngelegt, setWirdAngelegt] = useState(false);
async function anlegen() {
if (objekttypId === "") {
onFehler("Objekttyp auswählen.");
return;
}
setWirdAngelegt(true);
try {
await apiRequest("/faecher", { method: "POST", body: { objekttyp_id: objekttypId, name } });
setName("");
await onGeaendert();
} catch {
onFehler("Fach konnte nicht angelegt werden.");
} finally {
setWirdAngelegt(false);
}
}
async function loeschen(id: number) {
try {
await apiRequest(`/faecher/${id}`, { method: "DELETE" });
await onGeaendert();
} catch {
onFehler("Fach konnte nicht gelöscht werden.");
}
}
function objekttypName(id: number) {
return objekttypen.find((o) => o.id === id)?.name ?? `#${id}`;
}
return (
<section className="section">
<h2>Fächer je Objekttyp</h2>
<div className="card">
{objekttypen.length === 0 ? (
<p className="text-muted">Erst einen Objekttyp anlegen.</p>
) : (
<div className="row" style={{ marginBottom: faecher.length ? "1rem" : 0 }}>
<select
className="input"
value={objekttypId}
onChange={(e) => setObjekttypId(Number(e.target.value) || "")}
>
<option value="">Objekttyp wählen</option>
{objekttypen.map((o) => (
<option key={o.id} value={o.id}>
{o.name}
</option>
))}
</select>
<input
className="input"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Fach-Name (z. B. Fach 1)"
/>
<button className="btn btn-primary" onClick={anlegen} disabled={!name || wirdAngelegt}>
Anlegen
</button>
</div>
)}
{faecher.length > 0 && (
<ul className="card-list">
{faecher.map((f) => (
<li key={f.id} className="row-between" style={{ padding: "0.4rem 0" }}>
<span>
{f.name} <span className="text-muted">({objekttypName(f.objekttyp_id)})</span>
</span>
<button className="btn btn-danger" onClick={() => loeschen(f.id)}>
Löschen
</button>
</li>
))}
</ul>
)}
</div>
</section>
);
}
+12 -14
View File
@@ -1,7 +1,8 @@
import { useState } from "react";
import { apiRequest } from "../../api/client";
import type { Beladungsvorlage, Material, Objekttyp } from "../../api/types";
import type { Beladungsvorlage, Fach, Material, Objekttyp } from "../../api/types";
import { FachAuswahl } from "./FachAuswahl";
interface PositionZeile {
materialId: number | "";
@@ -15,11 +16,12 @@ interface Props {
vorlagen: Beladungsvorlage[];
objekttypen: Objekttyp[];
materialien: Material[];
faecher: Fach[];
onGeaendert: () => Promise<void>;
onFehler: (text: string) => void;
}
export function VorlageSection({ vorlagen, objekttypen, materialien, onGeaendert, onFehler }: Props) {
export function VorlageSection({ vorlagen, objekttypen, materialien, faecher, onGeaendert, onFehler }: Props) {
const [name, setName] = useState("");
const [objekttypId, setObjekttypId] = useState<number | "">("");
const [zeilen, setZeilen] = useState<PositionZeile[]>([LEERE_ZEILE]);
@@ -129,12 +131,10 @@ export function VorlageSection({ vorlagen, objekttypen, materialien, onGeaendert
</option>
))}
</select>
<input
className="input"
style={{ width: "8rem" }}
value={zeile.fach}
onChange={(e) => zeileAendern(i, { fach: e.target.value })}
placeholder="Fach"
<FachAuswahl
faecher={faecher.filter((f) => f.objekttyp_id === objekttypId)}
wert={zeile.fach}
onAendern={(wert) => zeileAendern(i, { fach: wert })}
/>
<input
className="input"
@@ -189,12 +189,10 @@ export function VorlageSection({ vorlagen, objekttypen, materialien, onGeaendert
</option>
))}
</select>
<input
className="input"
style={{ width: "8rem" }}
value={zeile.fach}
onChange={(e) => neueVersionZeileAendern(i, { fach: e.target.value })}
placeholder="Fach"
<FachAuswahl
faecher={faecher.filter((f) => f.objekttyp_id === v.objekttyp_id)}
wert={zeile.fach}
onAendern={(wert) => neueVersionZeileAendern(i, { fach: wert })}
/>
<input
className="input"