Kontoformular zweispaltig
Konto bearbeiten und Neues Konto zeigen ihre vierzehn Felder in zwei Spalten, damit alles ohne vertikales Scrollen auf einen Bildschirm passt. - Neue Formularvariante .formular.zweispaltig: Grid mit zwei Spalten, je Feld ein .feld aus Beschriftung, Eingabe und Hinweis. Überschriften, Knopfleiste und .feld.ganz laufen über beide Spalten, unter 720 px fällt das Formular auf eine Spalte zurück. - Feldreihenfolge zeilenweise; Namen, Werte und Feld-IDs unverändert. - E2E-Test: Kontoart steht rechts neben dem Namen, das Formular endet oberhalb der Fensterhöhe. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
2c86e8522b
commit
a22c33bb12
4 changed files with 159 additions and 61 deletions
17
Changes.md
17
Changes.md
|
|
@ -1,5 +1,22 @@
|
|||
# Änderungen
|
||||
|
||||
## Kontoformular zweispaltig (17.08.2026)
|
||||
|
||||
*Konto bearbeiten* und *Neues Konto* zeigen ihre Felder in zwei Spalten. Alle Eingaben
|
||||
stehen damit auf einem Bildschirm, ohne zu scrollen; bisher lagen die vierzehn Felder
|
||||
untereinander.
|
||||
|
||||
- Neue Formularvariante `.formular.zweispaltig` (`public/static/app.css`): ein Grid mit
|
||||
zwei Spalten, je Feld ein `.feld` aus Beschriftung, Eingabe und Hinweis. Überschriften,
|
||||
Knopfleiste und `.feld.ganz` laufen über beide Spalten. Unter 720 px Fensterbreite fällt
|
||||
das Formular auf eine Spalte zurück.
|
||||
- Feldreihenfolge zeilenweise: Name/Kontoart, Kontengruppe/Beschreibung, Bank/Kontonummer,
|
||||
IBAN/BIC, Anfangssaldo/Kreditlimit, Sortierung/Kennzeichen. Namen, Werte und Feld-IDs
|
||||
bleiben unverändert.
|
||||
- Der Bereich *Konto löschen* darunter bekommt dieselbe Breite, bleibt aber einspaltig.
|
||||
- E2E-Test in `tests/e2e/specs/stammdaten.spec.js`: Kontoart steht rechts neben dem Namen,
|
||||
und das Formular endet oberhalb der Fensterhöhe.
|
||||
|
||||
## Import und Export in zwei Formaten (17.08.2026)
|
||||
|
||||
Aus der Kachel *QIF-Import* wird **Import/Export** mit je einem Knopf für beide Richtungen.
|
||||
|
|
|
|||
|
|
@ -1231,6 +1231,45 @@ table.kostentabelle .gruppenzeile th { padding-top: .7rem; font-weight: 600; }
|
|||
margin-top: .6rem;
|
||||
}
|
||||
|
||||
/* Zweispaltiges Formular: je Feld ein .feld mit Beschriftung, Eingabe und
|
||||
Hinweis, zwei davon nebeneinander. Lange Formulare – das Kontoformular etwa –
|
||||
bleiben so ohne vertikales Scrollen einsehbar. */
|
||||
.formular.zweispaltig {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
align-items: start;
|
||||
gap: 1rem 1.5rem;
|
||||
}
|
||||
|
||||
.formular.breit.zweispaltig { max-width: 900px; }
|
||||
|
||||
.formular.zweispaltig .feld {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: .3rem;
|
||||
}
|
||||
|
||||
.formular.zweispaltig .feld label { margin-top: 0; }
|
||||
|
||||
.formular.zweispaltig .feld .feldhinweis { margin-top: 0; }
|
||||
|
||||
/* Über beide Spalten: verstecktes CSRF-Feld, Überschriften, Knopfleiste und
|
||||
Felder, die für eine halbe Zeile zu breit sind. */
|
||||
.formular.zweispaltig > input[type="hidden"],
|
||||
.formular.zweispaltig > h3,
|
||||
.formular.zweispaltig > p,
|
||||
.formular.zweispaltig > .knopfleiste,
|
||||
.formular.zweispaltig .feld.ganz { grid-column: 1 / -1; }
|
||||
|
||||
/* Die Abstände kommen im Grid aus dem gap – eigene Ränder addierten sich dazu. */
|
||||
.formular.zweispaltig > h3 { margin: 0; }
|
||||
.formular.zweispaltig > p { margin: 0; }
|
||||
.formular.zweispaltig > .knopfleiste { margin-top: .5rem; }
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.formular.zweispaltig { grid-template-columns: 1fr; }
|
||||
}
|
||||
|
||||
.formular label.ankreuzfeld {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
|
|||
|
|
@ -19,12 +19,15 @@ include __DIR__ . '/../layout/kopf.php';
|
|||
<a href="/konten" class="knopf">Zurück zur Kontenübersicht</a>
|
||||
</div>
|
||||
|
||||
<form method="post" action="<?= View::h($ziel) ?>" class="formular breit">
|
||||
<form method="post" action="<?= View::h($ziel) ?>" class="formular breit zweispaltig">
|
||||
<input type="hidden" name="csrf_token" value="<?= View::h($csrfToken) ?>">
|
||||
|
||||
<div class="feld">
|
||||
<label for="name">Name des Kontos</label>
|
||||
<input type="text" id="name" name="name" value="<?= $wert('name') ?>" required autofocus>
|
||||
</div>
|
||||
|
||||
<div class="feld">
|
||||
<label for="kontoart">Kontoart</label>
|
||||
<select id="kontoart" name="kontoart">
|
||||
<?php foreach (Konto::ARTEN as $schluessel => $beschriftung): ?>
|
||||
|
|
@ -34,7 +37,9 @@ include __DIR__ . '/../layout/kopf.php';
|
|||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="feld">
|
||||
<label for="kontengruppe_id">Kontengruppe</label>
|
||||
<select id="kontengruppe_id" name="kontengruppe_id">
|
||||
<option value="">Nicht gruppiert</option>
|
||||
|
|
@ -45,19 +50,34 @@ include __DIR__ . '/../layout/kopf.php';
|
|||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="feld">
|
||||
<label for="beschreibung">Beschreibung <span class="optional">(optional)</span></label>
|
||||
<input type="text" id="beschreibung" name="beschreibung" value="<?= $wert('beschreibung') ?>">
|
||||
</div>
|
||||
|
||||
<div class="feld">
|
||||
<label for="bankname">Bank <span class="optional">(optional)</span></label>
|
||||
<input type="text" id="bankname" name="bankname" value="<?= $wert('bankname') ?>">
|
||||
</div>
|
||||
|
||||
<div class="feld">
|
||||
<label for="kontonummer">Kontonummer <span class="optional">(optional)</span></label>
|
||||
<input type="text" id="kontonummer" name="kontonummer" value="<?= $wert('kontonummer') ?>">
|
||||
</div>
|
||||
|
||||
<div class="feld">
|
||||
<label for="iban">IBAN <span class="optional">(optional)</span></label>
|
||||
<input type="text" id="iban" name="iban" value="<?= $wert('iban') ?>">
|
||||
</div>
|
||||
|
||||
<div class="feld">
|
||||
<label for="bic">BIC <span class="optional">(optional)</span></label>
|
||||
<input type="text" id="bic" name="bic" value="<?= $wert('bic') ?>">
|
||||
</div>
|
||||
|
||||
<div class="feld">
|
||||
<label for="anfangssaldo">Anfangssaldo</label>
|
||||
<input type="text" id="anfangssaldo" name="anfangssaldo" inputmode="decimal"
|
||||
value="<?= View::h(View::betrag($konto['anfangssaldo'] ?? 0, false)) ?>">
|
||||
|
|
@ -65,19 +85,22 @@ include __DIR__ . '/../layout/kopf.php';
|
|||
Kontostand vor der ersten erfassten Buchung. Der QIF-Export enthält ihn nicht –
|
||||
nach einem Import wird er hier je Konto nachgetragen.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="feld">
|
||||
<label for="kreditlimit">Kreditlimit <span class="optional">(optional)</span></label>
|
||||
<input type="text" id="kreditlimit" name="kreditlimit" inputmode="decimal"
|
||||
value="<?= $konto === null || $konto['kreditlimit'] === null
|
||||
? '' : View::h(View::betrag($konto['kreditlimit'], false)) ?>">
|
||||
</div>
|
||||
|
||||
<label for="beschreibung">Beschreibung <span class="optional">(optional)</span></label>
|
||||
<input type="text" id="beschreibung" name="beschreibung" value="<?= $wert('beschreibung') ?>">
|
||||
|
||||
<div class="feld">
|
||||
<label for="sortierung">Sortierung</label>
|
||||
<input type="number" id="sortierung" name="sortierung" value="<?= $wert('sortierung', '0') ?>" step="1">
|
||||
<p class="feldhinweis">Kleinere Werte stehen in der Kontenübersicht weiter oben.</p>
|
||||
</div>
|
||||
|
||||
<div class="feld">
|
||||
<label class="ankreuzfeld">
|
||||
<input type="checkbox" name="aktiv" value="1" <?= $neu || (int)$konto['aktiv'] === 1 ? 'checked' : '' ?>>
|
||||
Konto ist aktiv
|
||||
|
|
@ -93,6 +116,7 @@ include __DIR__ . '/../layout/kopf.php';
|
|||
darstellen – durchlaufende Auslagen etwa –, bleiben so aus der Gesamtsumme heraus,
|
||||
erscheinen aber weiter in der Übersicht.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="knopfleiste">
|
||||
<button type="submit" class="knopf primaer"><?= $neu ? 'Konto anlegen' : 'Speichern' ?></button>
|
||||
|
|
@ -101,7 +125,7 @@ include __DIR__ . '/../layout/kopf.php';
|
|||
</form>
|
||||
|
||||
<?php if (!$neu): ?>
|
||||
<form method="post" action="/konten/<?= (int)$konto['id'] ?>/loeschen" class="formular breit"
|
||||
<form method="post" action="/konten/<?= (int)$konto['id'] ?>/loeschen" class="formular breit zweispaltig"
|
||||
data-bestaetigen="Konto „<?= View::h($konto['name']) ?>“ wirklich löschen?">
|
||||
<input type="hidden" name="csrf_token" value="<?= View::h($csrfToken) ?>">
|
||||
<h3>Konto löschen</h3>
|
||||
|
|
|
|||
|
|
@ -89,6 +89,24 @@ test.describe('Stammdaten', () => {
|
|||
await expect(page.locator('tr', { hasText: name })).not.toContainText('ohne Σ')
|
||||
})
|
||||
|
||||
// Das Kontoformular steht zweispaltig, damit alle Felder ohne vertikales
|
||||
// Scrollen sichtbar sind. Geprüft wird an den Kästen: Kontoart steht rechts
|
||||
// neben dem Namen, und das Formular endet oberhalb der Fensterhöhe.
|
||||
test('Kontoformular steht zweispaltig und ohne Scrollen', async ({ page }) => {
|
||||
await page.setViewportSize({ width: 1280, height: 900 })
|
||||
await page.goto('/konten/neu')
|
||||
|
||||
const name = await page.locator('#name').boundingBox()
|
||||
const kontoart = await page.locator('#kontoart').boundingBox()
|
||||
|
||||
// Gleiche Zeile, zweite Spalte.
|
||||
expect(Math.abs(name.y - kontoart.y)).toBeLessThan(5)
|
||||
expect(kontoart.x).toBeGreaterThan(name.x + name.width)
|
||||
|
||||
const knopf = await page.locator('.formular button[type="submit"]').first().boundingBox()
|
||||
expect(knopf.y + knopf.height).toBeLessThan(900)
|
||||
})
|
||||
|
||||
test('Kategorie mit Unterkategorie anlegen', async ({ page }) => {
|
||||
const oben = `E2E Wohnen ${MARKE}`
|
||||
const unten = `Strom ${MARKE}`
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue