initial commit, boilerplate stuff

This commit is contained in:
khannurien
2026-03-15 17:15:46 +00:00
commit 6207a7549f
52 changed files with 4400 additions and 0 deletions

169
src/pages/DumpEdit.tsx Normal file
View 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>
);
}