Web-UI: professionelles Redesign (Icons statt Emojis) + neue Features
Design: - Neues Icon-Set (Inline-SVG, Feather-Stil), keine Emojis mehr - Kohaerentes Design-System (Sidebar-Layout, Palette, Spacing, Light/Dark) - Alle Seiten ueberarbeitet (Login, Dashboard, Produkte, Formular, Ein-/Auslagern, Lagerorte, Benutzer, Einkaufsliste) Neue Features: - Gruppen-Verwaltung (anlegen/bearbeiten/loeschen, Produktzahl + Bestand, Gruppen-Mindestbestand) inkl. Einkaufsliste - Einfache Gruppen-Auto-Zuordnung aus OFF-Kategorie im Produktformular - Verlauf-Seite (Bewegungen: wer/was/wann) via neuem /movements-Endpoint - Einstellungen-Seite (Ablauf-Warnfrist) - Lagerorte mit optional uebergeordnetem Ort (verschachtelbar) Backend: - groups: PATCH + Anreicherung (product_count, stock) - views: /shopping-list/groups, /movements - schemas: GroupUpdate, GroupShoppingItem, MovementOut Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
64
web/src/pages/Settings.jsx
Normal file
64
web/src/pages/Settings.jsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { api } from "../api";
|
||||
import Icon from "../components/Icon";
|
||||
|
||||
const EXPIRY_KEY = "expiry_warning_days";
|
||||
|
||||
export default function Settings() {
|
||||
const [days, setDays] = useState("");
|
||||
const [error, setError] = useState(null);
|
||||
const [info, setInfo] = useState(null);
|
||||
const [busy, setBusy] = useState(false);
|
||||
|
||||
async function load() {
|
||||
try {
|
||||
const settings = await api.listSettings();
|
||||
const row = settings.find((s) => s.key === EXPIRY_KEY);
|
||||
setDays(row ? row.value : "7");
|
||||
} catch (err) {
|
||||
setError(err.message);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => { load(); }, []);
|
||||
|
||||
async function save(e) {
|
||||
e.preventDefault();
|
||||
setError(null); setInfo(null); setBusy(true);
|
||||
try {
|
||||
await api.setSetting(EXPIRY_KEY, String(parseInt(days, 10)));
|
||||
setInfo("Gespeichert.");
|
||||
} catch (err) {
|
||||
setError(err.message);
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="page-head">
|
||||
<div>
|
||||
<h1>Einstellungen</h1>
|
||||
<div className="sub">Allgemeine Konfiguration der Anwendung</div>
|
||||
</div>
|
||||
</div>
|
||||
{error && <div className="alert error"><Icon name="alert" size={16} />{error}</div>}
|
||||
{info && <div className="alert ok"><Icon name="check" size={16} />{info}</div>}
|
||||
|
||||
<form className="card form-narrow" onSubmit={save}>
|
||||
<div className="card-head"><Icon name="clock" /><h2>Ablaufwarnung</h2></div>
|
||||
<label>
|
||||
Vorwarnzeit (Tage)
|
||||
<input type="number" min="0" step="1" value={days}
|
||||
onChange={(e) => setDays(e.target.value)} style={{ maxWidth: 160 }} />
|
||||
</label>
|
||||
<p className="muted small mt-0">
|
||||
Chargen, deren Mindesthaltbarkeitsdatum innerhalb dieser Frist liegt (oder
|
||||
bereits überschritten ist), erscheinen auf der Übersicht unter „Bald ablaufend".
|
||||
</p>
|
||||
<button className="btn primary" disabled={busy}>{busy ? "Speichern…" : "Speichern"}</button>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user