85 lines
3.9 KiB
TypeScript
85 lines
3.9 KiB
TypeScript
|
|
"use client";
|
||
|
|
|
||
|
|
import Link from "next/link";
|
||
|
|
import { trpc } from "@/lib/trpc";
|
||
|
|
|
||
|
|
function statusLabel(status: string) {
|
||
|
|
if (status === "accepted") return "übernommen";
|
||
|
|
if (status === "rejected") return "abgelehnt";
|
||
|
|
if (status === "pending") return "in Prüfung";
|
||
|
|
return status;
|
||
|
|
}
|
||
|
|
|
||
|
|
export default function AktivitaetsfeedPage() {
|
||
|
|
const utils = trpc.useUtils();
|
||
|
|
const feedQuery = trpc.health.adminActivityFeed.useQuery();
|
||
|
|
const reviewStoreChange = trpc.health.reviewStoreChangeRequest.useMutation({
|
||
|
|
onSuccess: async () => {
|
||
|
|
await utils.health.adminActivityFeed.invalidate();
|
||
|
|
await utils.health.listStores.invalidate();
|
||
|
|
},
|
||
|
|
});
|
||
|
|
const items = feedQuery.data ?? [];
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="mx-auto max-w-5xl space-y-5 pb-24">
|
||
|
|
<header className="rounded-[2rem] bg-white p-6 shadow-sm ring-1 ring-gray-100">
|
||
|
|
<p className="text-sm font-black uppercase text-[#0F766E]">Admin</p>
|
||
|
|
<h1 className="mt-2 text-4xl font-black tracking-normal text-gray-950">Activity Feed</h1>
|
||
|
|
<p className="mt-2 max-w-2xl text-sm font-semibold text-gray-400">
|
||
|
|
Eingereichte Rezeptvorschläge, Marktänderungen und weitere prüfpflichtige Aktionen an einem Ort.
|
||
|
|
</p>
|
||
|
|
</header>
|
||
|
|
|
||
|
|
<section className="rounded-[2rem] bg-white p-5 shadow-sm ring-1 ring-gray-100">
|
||
|
|
<div className="space-y-3">
|
||
|
|
{items.map((item) => (
|
||
|
|
<div key={`${item.type}-${item.id}`} className="rounded-3xl bg-gray-50 p-4">
|
||
|
|
<div className="flex flex-wrap items-start justify-between gap-3">
|
||
|
|
<div>
|
||
|
|
<p className="text-xs font-black uppercase text-[#0F766E]">
|
||
|
|
{item.type === "recipe_submission" ? "Rezeptvorschlag" : "Marktänderung"}
|
||
|
|
</p>
|
||
|
|
<h2 className="mt-1 text-lg font-black text-gray-950">{item.title}</h2>
|
||
|
|
<p className="mt-1 text-sm font-bold text-gray-400">{item.actorName}{item.actorEmail ? ` · ${item.actorEmail}` : ""}</p>
|
||
|
|
<p className="mt-1 text-sm font-semibold text-gray-500">{item.detail}</p>
|
||
|
|
</div>
|
||
|
|
<div className="text-right">
|
||
|
|
<span className={`rounded-full px-3 py-1 text-xs font-black ${
|
||
|
|
item.status === "accepted"
|
||
|
|
? "bg-emerald-100 text-emerald-700"
|
||
|
|
: item.status === "rejected"
|
||
|
|
? "bg-red-100 text-red-700"
|
||
|
|
: "bg-amber-100 text-amber-700"
|
||
|
|
}`}>
|
||
|
|
{statusLabel(item.status)}
|
||
|
|
</span>
|
||
|
|
<p className="mt-2 text-xs font-bold text-gray-400">{new Date(item.createdAt).toLocaleString("de-DE")}</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div className="mt-3 flex flex-wrap gap-2">
|
||
|
|
<Link href={item.href} className="rounded-full bg-white px-4 py-2 text-xs font-black text-gray-700">
|
||
|
|
Öffnen
|
||
|
|
</Link>
|
||
|
|
{item.type === "store_change" && item.status === "pending" ? (
|
||
|
|
<>
|
||
|
|
<button type="button" onClick={() => reviewStoreChange.mutate({ id: item.id, action: "accept" })} className="rounded-full bg-[#0F766E] px-4 py-2 text-xs font-black text-white">
|
||
|
|
Übernehmen
|
||
|
|
</button>
|
||
|
|
<button type="button" onClick={() => reviewStoreChange.mutate({ id: item.id, action: "reject" })} className="rounded-full bg-red-50 px-4 py-2 text-xs font-black text-red-700">
|
||
|
|
Ablehnen
|
||
|
|
</button>
|
||
|
|
</>
|
||
|
|
) : null}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
))}
|
||
|
|
{!items.length && !feedQuery.isLoading ? (
|
||
|
|
<p className="rounded-3xl bg-gray-50 p-4 text-sm font-bold text-gray-400">Aktuell gibt es keine Einträge.</p>
|
||
|
|
) : null}
|
||
|
|
</div>
|
||
|
|
</section>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|