Init first files
This commit is contained in:
114
frontend/src/pages/TargetsPage.jsx
Normal file
114
frontend/src/pages/TargetsPage.jsx
Normal file
@@ -0,0 +1,114 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { apiFetch } from "../api";
|
||||
import { useAuth } from "../state";
|
||||
|
||||
const emptyForm = {
|
||||
name: "",
|
||||
host: "",
|
||||
port: 5432,
|
||||
dbname: "",
|
||||
username: "",
|
||||
password: "",
|
||||
sslmode: "prefer",
|
||||
tags: {},
|
||||
};
|
||||
|
||||
export function TargetsPage() {
|
||||
const { tokens, refresh, me } = useAuth();
|
||||
const [targets, setTargets] = useState([]);
|
||||
const [form, setForm] = useState(emptyForm);
|
||||
const [error, setError] = useState("");
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
const canManage = me?.role === "admin" || me?.role === "operator";
|
||||
|
||||
const load = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
setTargets(await apiFetch("/targets", {}, tokens, refresh));
|
||||
setError("");
|
||||
} catch (e) {
|
||||
setError(String(e.message || e));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
load();
|
||||
}, []);
|
||||
|
||||
const createTarget = async (e) => {
|
||||
e.preventDefault();
|
||||
try {
|
||||
await apiFetch("/targets", { method: "POST", body: JSON.stringify(form) }, tokens, refresh);
|
||||
setForm(emptyForm);
|
||||
await load();
|
||||
} catch (e) {
|
||||
setError(String(e.message || e));
|
||||
}
|
||||
};
|
||||
|
||||
const deleteTarget = async (id) => {
|
||||
if (!confirm("Target löschen?")) return;
|
||||
try {
|
||||
await apiFetch(`/targets/${id}`, { method: "DELETE" }, tokens, refresh);
|
||||
await load();
|
||||
} catch (e) {
|
||||
setError(String(e.message || e));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>Targets Management</h2>
|
||||
{error && <div className="card error">{error}</div>}
|
||||
{canManage && (
|
||||
<form className="card grid two" onSubmit={createTarget}>
|
||||
<input placeholder="Name" value={form.name} onChange={(e) => setForm({ ...form, name: e.target.value })} required />
|
||||
<input placeholder="Host" value={form.host} onChange={(e) => setForm({ ...form, host: e.target.value })} required />
|
||||
<input placeholder="Port" value={form.port} onChange={(e) => setForm({ ...form, port: Number(e.target.value) })} type="number" />
|
||||
<input placeholder="DB Name" value={form.dbname} onChange={(e) => setForm({ ...form, dbname: e.target.value })} required />
|
||||
<input placeholder="Username" value={form.username} onChange={(e) => setForm({ ...form, username: e.target.value })} required />
|
||||
<input placeholder="Passwort" type="password" value={form.password} onChange={(e) => setForm({ ...form, password: e.target.value })} required />
|
||||
<select value={form.sslmode} onChange={(e) => setForm({ ...form, sslmode: e.target.value })}>
|
||||
<option value="disable">disable</option>
|
||||
<option value="prefer">prefer</option>
|
||||
<option value="require">require</option>
|
||||
</select>
|
||||
<button>Target anlegen</button>
|
||||
</form>
|
||||
)}
|
||||
<div className="card">
|
||||
{loading ? (
|
||||
<p>Lade Targets...</p>
|
||||
) : (
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Host</th>
|
||||
<th>DB</th>
|
||||
<th>Aktionen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{targets.map((t) => (
|
||||
<tr key={t.id}>
|
||||
<td>{t.name}</td>
|
||||
<td>{t.host}:{t.port}</td>
|
||||
<td>{t.dbname}</td>
|
||||
<td>
|
||||
<Link to={`/targets/${t.id}`}>Details</Link>{" "}
|
||||
{canManage && <button onClick={() => deleteTarget(t.id)}>Delete</button>}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user