initial commit, boilerplate stuff
This commit is contained in:
169
src/pages/DumpEdit.tsx
Normal file
169
src/pages/DumpEdit.tsx
Normal file
@@ -0,0 +1,169 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { Link, useNavigate, useParams } from "react-router";
|
||||
|
||||
import { API_URL } from "../config/api.ts";
|
||||
|
||||
import type { Dump, UpdateDumpRequest } from "../model.ts";
|
||||
|
||||
import { useRequiredAuth } from "../hooks/useAuth.ts";
|
||||
|
||||
type DumpEditState =
|
||||
| { status: "loading" }
|
||||
| { status: "error"; error: string }
|
||||
| { status: "loaded"; dump: Dump };
|
||||
|
||||
export function DumpEdit() {
|
||||
const { selectedDump } = useParams();
|
||||
const navigate = useNavigate();
|
||||
const { authFetch } = useRequiredAuth();
|
||||
|
||||
const [state, setState] = useState<DumpEditState>({ status: "loading" });
|
||||
const [title, setTitle] = useState("");
|
||||
const [description, setDescription] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
if (!selectedDump) return;
|
||||
|
||||
setState({ status: "loading" });
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
const res = await fetch(`${API_URL}/api/dumps/${selectedDump}`);
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
|
||||
const apiResponse = await res.json();
|
||||
|
||||
if (apiResponse.success) {
|
||||
const dump: Dump = apiResponse.data;
|
||||
setTitle(dump.title);
|
||||
setDescription(dump.description ?? "");
|
||||
setState({ status: "loaded", dump });
|
||||
} else {
|
||||
setState({
|
||||
status: "error",
|
||||
error: apiResponse.error.message,
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
setState({
|
||||
status: "error",
|
||||
error: err instanceof Error ? err.message : "Load failed",
|
||||
});
|
||||
}
|
||||
})();
|
||||
}, [selectedDump]);
|
||||
|
||||
const handleSave = async () => {
|
||||
if (state.status !== "loaded") return;
|
||||
|
||||
const body: UpdateDumpRequest = {
|
||||
title,
|
||||
description: description || undefined,
|
||||
};
|
||||
|
||||
const res = await authFetch(`${API_URL}/api/dumps/${state.dump.id}`, {
|
||||
method: "PUT",
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
setState({
|
||||
status: "error",
|
||||
error: `Update failed (${res.status})`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
navigate(`/dumps/${state.dump.id}`);
|
||||
};
|
||||
|
||||
const handleDelete = async () => {
|
||||
if (state.status !== "loaded") return;
|
||||
|
||||
const res = await authFetch(`${API_URL}/api/dumps/${state.dump.id}`, {
|
||||
method: "DELETE",
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
setState({
|
||||
status: "error",
|
||||
error: `Delete failed (${res.status})`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
navigate("/");
|
||||
};
|
||||
|
||||
if (state.status === "loading") {
|
||||
return <div className="loading">Loading dump...</div>;
|
||||
}
|
||||
|
||||
if (state.status === "error") {
|
||||
return (
|
||||
<div className="error-container">
|
||||
<h2>Error</h2>
|
||||
<p>{state.error}</p>
|
||||
<button type="button" onClick={() => globalThis.location.reload()}>
|
||||
Retry
|
||||
</button>
|
||||
<p>
|
||||
<Link to="/">← Back to all dumps</Link>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="dump-container">
|
||||
<div className="dump-meta">
|
||||
<h1>Edit Dump</h1>
|
||||
</div>
|
||||
|
||||
<form
|
||||
className="dump-form"
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
handleSave();
|
||||
}}
|
||||
>
|
||||
<div className="form-group">
|
||||
<label htmlFor="title">
|
||||
<strong>Title</strong>
|
||||
</label>
|
||||
<input
|
||||
id="title"
|
||||
type="text"
|
||||
value={title}
|
||||
onChange={(e) => setTitle(e.currentTarget.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="description">
|
||||
<strong>Description (optional)</strong>
|
||||
</label>
|
||||
<textarea
|
||||
id="description"
|
||||
value={description}
|
||||
onChange={(e) => setDescription(e.currentTarget.value)}
|
||||
rows={3}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="dump-actions">
|
||||
<button type="submit">Save</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleDelete}
|
||||
style={{ backgroundColor: "#a02b2b" }}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
<Link to={`/dumps/${state.dump.id}`}>Cancel</Link>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user