The changes split large features into smaller, reusable components like `AdminPanel`, `LoginPage`, `TopBar`, `PasswordModal`, and `ChipModal`. Utility functions such as `cycleTag` and `chipStorage` were extracted for better organization. This improves the code's readability, maintainability, and scalability.
136 lines
4.2 KiB
JavaScript
136 lines
4.2 KiB
JavaScript
import React, { useEffect, useState } from "react";
|
|
import { api } from "../api/client";
|
|
import { styles } from "../styles/styles";
|
|
import { stylesTokens } from "../styles/theme";
|
|
|
|
export default function AdminPanel() {
|
|
const [users, setUsers] = useState([]);
|
|
|
|
const [open, setOpen] = useState(false);
|
|
const [email, setEmail] = useState("");
|
|
const [password, setPassword] = useState("");
|
|
const [role, setRole] = useState("user");
|
|
const [msg, setMsg] = useState("");
|
|
|
|
const loadUsers = async () => {
|
|
const u = await api("/admin/users");
|
|
setUsers(u);
|
|
};
|
|
|
|
useEffect(() => {
|
|
loadUsers().catch(() => {});
|
|
}, []);
|
|
|
|
const resetForm = () => {
|
|
setEmail("");
|
|
setPassword("");
|
|
setRole("user");
|
|
};
|
|
|
|
const createUser = async () => {
|
|
setMsg("");
|
|
try {
|
|
await api("/admin/users", {
|
|
method: "POST",
|
|
body: JSON.stringify({ email, password, role }),
|
|
});
|
|
setMsg("✅ User erstellt.");
|
|
await loadUsers();
|
|
resetForm();
|
|
setOpen(false);
|
|
} catch (e) {
|
|
setMsg("❌ Fehler: " + (e?.message || "unknown"));
|
|
}
|
|
};
|
|
|
|
const closeModal = () => {
|
|
setOpen(false);
|
|
setMsg("");
|
|
};
|
|
|
|
return (
|
|
<div style={styles.adminWrap}>
|
|
<div style={styles.adminTop}>
|
|
<div style={styles.adminTitle}>Admin Dashboard</div>
|
|
<button onClick={() => setOpen(true)} style={styles.primaryBtn}>
|
|
+ User anlegen
|
|
</button>
|
|
</div>
|
|
|
|
<div style={{ marginTop: 12, fontWeight: 900, color: stylesTokens.textGold }}>
|
|
Vorhandene User
|
|
</div>
|
|
|
|
<div style={{ marginTop: 8, display: "grid", gap: 8 }}>
|
|
{users.map((u) => (
|
|
<div key={u.id} style={styles.userRow}>
|
|
<div style={{ color: stylesTokens.textMain }}>{u.email}</div>
|
|
<div style={{ textAlign: "center", fontWeight: 900, color: stylesTokens.textGold }}>
|
|
{u.role}
|
|
</div>
|
|
<div style={{ textAlign: "center", opacity: 0.85, color: stylesTokens.textMain }}>
|
|
{u.disabled ? "disabled" : "active"}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
{open && (
|
|
<div style={styles.modalOverlay} onMouseDown={closeModal}>
|
|
<div style={styles.modalCard} onMouseDown={(e) => e.stopPropagation()}>
|
|
<div style={styles.modalHeader}>
|
|
<div style={{ fontWeight: 1000, color: stylesTokens.textGold }}>
|
|
Neuen User anlegen
|
|
</div>
|
|
<button onClick={closeModal} style={styles.modalCloseBtn} aria-label="Schließen">
|
|
✕
|
|
</button>
|
|
</div>
|
|
|
|
<div style={{ marginTop: 12, display: "grid", gap: 10 }}>
|
|
<input
|
|
value={email}
|
|
onChange={(e) => setEmail(e.target.value)}
|
|
placeholder="Email"
|
|
style={styles.input}
|
|
autoFocus
|
|
/>
|
|
<input
|
|
value={password}
|
|
onChange={(e) => setPassword(e.target.value)}
|
|
placeholder="Initial Passwort"
|
|
type="password"
|
|
style={styles.input}
|
|
/>
|
|
<select value={role} onChange={(e) => setRole(e.target.value)} style={styles.input}>
|
|
<option value="user">user</option>
|
|
<option value="admin">admin</option>
|
|
</select>
|
|
|
|
{msg && <div style={{ opacity: 0.9, color: stylesTokens.textMain }}>{msg}</div>}
|
|
|
|
<div style={{ display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 4 }}>
|
|
<button
|
|
onClick={() => {
|
|
resetForm();
|
|
setMsg("");
|
|
}}
|
|
style={styles.secondaryBtn}
|
|
>
|
|
Leeren
|
|
</button>
|
|
<button onClick={createUser} style={styles.primaryBtn}>
|
|
User erstellen
|
|
</button>
|
|
</div>
|
|
|
|
<div style={{ fontSize: 12, opacity: 0.75, color: stylesTokens.textDim }}>
|
|
Tipp: Klick auf Item: Grün → Rot → Grau → Leer
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
} |