Refactor code structure for improved readability and maintainability

This commit is contained in:
2026-02-01 02:28:31 +00:00
parent 7b0ca40c4f
commit 73025c84c5
10 changed files with 413 additions and 87 deletions

View File

@@ -7,19 +7,30 @@ export default function LicenseManager() {
const { user, token, authFetch, login, register: registerUser, loading: authLoading } = useAuth();
const [licenses, setLicenses] = useState([]);
const [plugins, setPlugins] = useState([]);
const [users, setUsers] = useState([]);
const [selectedPluginId, setSelectedPluginId] = useState("");
const [selectedOwnerId, setSelectedOwnerId] = useState("");
const [label, setLabel] = useState("");
const [note, setNote] = useState("");
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [creating, setCreating] = useState(false);
const [creatingLicense, setCreatingLicense] = useState(false);
const [creatingUser, setCreatingUser] = useState(false);
const [refreshing, setRefreshing] = useState(false);
const [lastSync, setLastSync] = useState(null);
const [formStatus, setFormStatus] = useState(null);
const [userFormStatus, setUserFormStatus] = useState(null);
const [verifyStatus, setVerifyStatus] = useState(null);
const [verifying, setVerifying] = useState(false);
const [verifyKey, setVerifyKey] = useState("");
const [verifyHostname, setVerifyHostname] = useState("");
const [newUserForm, setNewUserForm] = useState({
username: "",
name: "",
email: "",
password: "",
isAdmin: false
});
const isAuthenticated = Boolean(user && token);
@@ -57,8 +68,50 @@ export default function LicenseManager() {
};
}, []);
useEffect(() => {
if (!isAuthenticated) {
setUsers([]);
setSelectedOwnerId("");
return;
}
if (user && !selectedOwnerId) {
setSelectedOwnerId(String(user.id));
}
}, [isAuthenticated, selectedOwnerId, user]);
useEffect(() => {
if (!isAuthenticated || !user) return;
if (!user.isAdmin) {
setUsers([user]);
return;
}
let cancelled = false;
async function loadUsers() {
try {
const response = await authFetch("/api/admin/users");
const data = await response.json().catch(() => ({}));
if (!response.ok) {
throw new Error(data.error || "Kon gebruikers niet laden.");
}
if (cancelled) return;
setUsers(data.items || []);
if (!selectedOwnerId && (data.items || []).length > 0) {
setSelectedOwnerId(String(data.items[0].id));
}
} catch (err) {
if (!cancelled) {
setFormStatus({ variant: "error", message: err.message });
}
}
}
loadUsers();
return () => {
cancelled = true;
};
}, [authFetch, isAuthenticated, selectedOwnerId, user]);
const refreshLicenses = useCallback(
async (showStatus = true) => {
async (showStatus = true, overrideUserId) => {
if (!token) {
setLicenses([]);
setLastSync(null);
@@ -72,7 +125,14 @@ export default function LicenseManager() {
}
setRefreshing(true);
try {
const response = await authFetch("/api/licenses");
const ownerIdToUse =
overrideUserId ||
(user?.isAdmin ? selectedOwnerId || (user ? String(user.id) : "") : user ? String(user.id) : "");
let url = "/api/licenses";
if (user?.isAdmin && ownerIdToUse) {
url += `?userId=${ownerIdToUse}`;
}
const response = await authFetch(url);
const data = await response.json().catch(() => ({}));
if (response.status === 401) {
throw new Error("Sessie verlopen, log opnieuw in.");
@@ -90,7 +150,7 @@ export default function LicenseManager() {
setRefreshing(false);
}
},
[authFetch, token]
[authFetch, selectedOwnerId, token, user]
);
useEffect(() => {
@@ -125,7 +185,7 @@ export default function LicenseManager() {
setFormStatus({ variant: "error", message: "Selecteer een plugin." });
return;
}
setCreating(true);
setCreatingLicense(true);
try {
const payload = {
label:
@@ -140,10 +200,14 @@ export default function LicenseManager() {
baseUrl: selectedPlugin.baseUrl
}
};
const requestBody = {
...payload,
userId: user?.isAdmin ? Number(selectedOwnerId || user.id) : undefined
};
const response = await authFetch("/api/licenses", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload)
body: JSON.stringify(requestBody)
});
const data = await response.json().catch(() => ({}));
if (response.status === 401) {
@@ -159,7 +223,34 @@ export default function LicenseManager() {
} catch (err) {
setFormStatus({ variant: "error", message: err.message });
} finally {
setCreating(false);
setCreatingLicense(false);
}
}
async function handleCreateUser(event) {
event.preventDefault();
setUserFormStatus(null);
setCreatingUser(true);
try {
const response = await authFetch("/api/admin/users", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(newUserForm)
});
const data = await response.json().catch(() => ({}));
if (!response.ok) {
throw new Error(data.error || "Gebruiker aanmaken mislukt.");
}
setUserFormStatus({ variant: "success", message: "Gebruiker aangemaakt." });
setNewUserForm({ username: "", name: "", email: "", password: "", isAdmin: false });
setUsers((prev) => [...prev, data.user]);
if (!selectedOwnerId) {
setSelectedOwnerId(String(data.user.id));
}
} catch (err) {
setUserFormStatus({ variant: "error", message: err.message });
} finally {
setCreatingUser(false);
}
}
@@ -236,6 +327,24 @@ export default function LicenseManager() {
<span>Actieve licenties: {licenses.length}</span>
<span>Laatste update: {formatDateTime(lastSync)}</span>
{user && <span>Ingelogd als: {user.email}</span>}
{user?.isAdmin && users.length > 0 && (
<label className="inline-field">
<span>Licenties gebruiker</span>
<select
value={selectedOwnerId}
onChange={(event) => {
setSelectedOwnerId(event.target.value);
refreshLicenses(true, event.target.value);
}}
>
{users.map((u) => (
<option key={u.id} value={u.id}>
{u.name} ({u.email})
</option>
))}
</select>
</label>
)}
</div>
{isLoadingState && <div className="state">Bezig met laden</div>}
@@ -283,8 +392,20 @@ export default function LicenseManager() {
rows={3}
/>
</label>
<button className="cta" type="submit" disabled={creating || !selectedPlugin}>
{creating ? "Aanmaken…" : "Licentie aanmaken"}
{user?.isAdmin && (
<label className="field">
<span>Licentie voor gebruiker</span>
<select value={selectedOwnerId} onChange={(event) => setSelectedOwnerId(event.target.value)}>
{users.map((u) => (
<option key={u.id} value={u.id}>
{u.name} ({u.email})
</option>
))}
</select>
</label>
)}
<button className="cta" type="submit" disabled={creatingLicense || !selectedPlugin}>
{creatingLicense ? "Aanmaken…" : "Licentie aanmaken"}
</button>
</form>
{formStatus && (
@@ -340,6 +461,69 @@ export default function LicenseManager() {
</article>
</section>
{user?.isAdmin && (
<section className="license-forms">
<article className="card">
<h2>Gebruiker toevoegen</h2>
<p className="hint">Admins kunnen extra gebruikers aanmaken en direct licenties toewijzen.</p>
<form className="form-grid" onSubmit={handleCreateUser}>
<label className="field">
<span>Gebruikersnaam</span>
<input
value={newUserForm.username}
onChange={(event) => setNewUserForm((prev) => ({ ...prev, username: event.target.value }))}
placeholder="gebruikersnaam"
/>
</label>
<label className="field">
<span>Naam</span>
<input
value={newUserForm.name}
onChange={(event) => setNewUserForm((prev) => ({ ...prev, name: event.target.value }))}
placeholder="Volledige naam"
/>
</label>
<label className="field">
<span>E-mail</span>
<input
type="email"
value={newUserForm.email}
onChange={(event) => setNewUserForm((prev) => ({ ...prev, email: event.target.value }))}
placeholder="naam@bedrijf.nl"
/>
</label>
<label className="field">
<span>Wachtwoord</span>
<input
type="password"
value={newUserForm.password}
onChange={(event) => setNewUserForm((prev) => ({ ...prev, password: event.target.value }))}
placeholder="Minimaal 8 karakters"
/>
</label>
<label className="checkbox-field">
<input
type="checkbox"
checked={newUserForm.isAdmin}
onChange={(event) =>
setNewUserForm((prev) => ({ ...prev, isAdmin: event.target.checked }))
}
/>
<span>Maak deze gebruiker admin</span>
</label>
<button className="cta" type="submit" disabled={creatingUser}>
{creatingUser ? "Bezig…" : "Gebruiker aanmaken"}
</button>
</form>
{userFormStatus && (
<div className={`state inline ${userFormStatus.variant === "error" ? "error" : "success"}`}>
{userFormStatus.message}
</div>
)}
</article>
</section>
)}
{isAuthenticated ? (
<section className="license-grid">
{sortedLicenses.length === 0 ? (