lomavuokraus/app/listings/mine/page.tsx
Tero Halla-aho 0bb709d9c5
Some checks failed
CI / checks (push) Has been cancelled
chore: fix audit alerts and formatting
2026-02-04 12:43:03 +02:00

141 lines
4.2 KiB
TypeScript

"use client";
import { useEffect, useState } from "react";
import Link from "next/link";
import { useI18n } from "../../components/I18nProvider";
type MyListing = {
id: string;
status: string;
translations: { title: string; slug: string; locale: string }[];
};
export default function MyListingsPage() {
const { t } = useI18n();
const [listings, setListings] = useState<MyListing[]>([]);
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(true);
const [message, setMessage] = useState<string | null>(null);
const [actionId, setActionId] = useState<string | null>(null);
useEffect(() => {
fetch("/api/listings/mine", { cache: "no-store" })
.then((res) => res.json())
.then((data) => {
if (data.error) {
setError(data.error);
} else {
setListings(data.listings ?? []);
}
})
.catch(() => setError("Failed to load"))
.finally(() => setLoading(false));
}, []);
async function removeListing(listingId: string) {
if (!window.confirm(t("removeConfirm"))) return;
setActionId(listingId);
setError(null);
setMessage(null);
try {
const res = await fetch("/api/listings/remove", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ listingId }),
});
const data = await res.json();
if (!res.ok) {
setError(data.error || "Failed to remove listing");
} else {
setMessage(t("removed"));
setListings((prev) =>
prev.map((l) =>
l.id === listingId ? { ...l, status: "REMOVED" } : l,
),
);
}
} catch (e) {
setError("Failed to remove listing");
} finally {
setActionId(null);
}
}
if (loading) {
return (
<main className="panel" style={{ maxWidth: 720, margin: "40px auto" }}>
<h1>{t("myListingsTitle")}</h1>
<p>{t("loading")}</p>
</main>
);
}
if (error) {
return (
<main className="panel" style={{ maxWidth: 720, margin: "40px auto" }}>
<h1>{t("myListingsTitle")}</h1>
<p style={{ color: "red" }}>{error}</p>
</main>
);
}
return (
<main className="panel" style={{ maxWidth: 720, margin: "40px auto" }}>
<h1>{t("myListingsTitle")}</h1>
{message ? <p style={{ color: "green" }}>{message}</p> : null}
<div style={{ marginBottom: 12 }}>
<Link href="/listings/new" className="button secondary">
{t("createNewListing")}
</Link>
</div>
{listings.length === 0 ? (
<p>
{t("noListings")} <Link href="/listings/new">{t("createOne")}</Link>.
</p>
) : (
<ul style={{ listStyle: "none", padding: 0, display: "grid", gap: 10 }}>
{listings.map((l) => (
<li
key={l.id}
style={{ border: "1px solid #ddd", borderRadius: 8, padding: 12 }}
>
<div>
<strong>{l.translations[0]?.title ?? "Listing"}</strong> {" "}
{t("statusLabel")}: {l.status}
</div>
<div style={{ fontSize: 12, color: "#666" }}>
{t("slugsLabel")}:{" "}
{l.translations
.map((t) => `${t.slug} (${t.locale})`)
.join(", ")}
</div>
<div style={{ marginTop: 8, display: "flex", gap: 8 }}>
<Link
href={`/listings/edit/${l.id}`}
className="button secondary"
>
{t("edit")}
</Link>
{l.status !== "DRAFT" ? (
<Link
href={`/listings/${l.translations[0]?.slug ?? ""}`}
className="button secondary"
>
{t("view")}
</Link>
) : null}
<button
className="button secondary"
onClick={() => removeListing(l.id)}
disabled={actionId === l.id}
>
{actionId === l.id ? t("removing") : t("remove")}
</button>
</div>
</li>
))}
</ul>
)}
</main>
);
}