hellth-hub/apps/admin/app/(authed)/profil/[handle]/page.tsx

114 lines
3.7 KiB
TypeScript
Raw Permalink Normal View History

"use client";
import { FireIcon, SparklesIcon } from "@heroicons/react/24/outline";
import { useParams } from "next/navigation";
import { DataPageLayout } from "@/app/_components/data-page-layout";
import { trpc } from "@/lib/trpc";
export default function PublicProfilePage() {
const params = useParams<{ handle: string }>();
const handleSlug = decodeURIComponent(params.handle ?? "");
const profileQuery = trpc.users.publicProfileByHandle.useQuery(
{ handle: handleSlug },
{ retry: false },
);
if (profileQuery.isLoading) {
return (
<DataPageLayout title="Profil">
<p className="text-sm text-gray-500">Profil wird geladen …</p>
</DataPageLayout>
);
}
if (profileQuery.isError || !profileQuery.data) {
return (
<DataPageLayout title="Profil nicht gefunden">
<div className="rounded-xl border border-gray-200 bg-white p-6 text-sm text-gray-600">
Dieses Profil gibt es nicht oder es ist privat. Nur Mitglieder, die ihr
Profil öffentlich geschaltet haben, sind hier sichtbar.
</div>
</DataPageLayout>
);
}
const profile = profileQuery.data;
const initial = (profile.username ?? "?").charAt(0).toUpperCase();
return (
<DataPageLayout title={profile.handle ?? profile.username ?? "Profil"}>
<div className="space-y-4">
<div className="flex items-center gap-4 rounded-xl border border-gray-200 bg-white p-5">
{profile.image ? (
// eslint-disable-next-line @next/next/no-img-element
<img
src={profile.image}
alt={profile.username ?? "Avatar"}
className="h-16 w-16 rounded-full object-cover"
/>
) : (
<div className="flex h-16 w-16 items-center justify-center rounded-full bg-[#0F766E] text-xl font-bold text-white">
{initial}
</div>
)}
<div className="min-w-0">
<p className="truncate text-lg font-semibold text-gray-900">
{profile.username}
<span className="font-normal text-gray-400">#{profile.discriminator}</span>
</p>
{profile.bio ? (
<p className="mt-1 whitespace-pre-line text-sm text-gray-600">{profile.bio}</p>
) : (
<p className="mt-1 text-sm text-gray-400">Keine Bio hinterlegt.</p>
)}
</div>
</div>
<div className="grid grid-cols-1 gap-3 sm:grid-cols-3">
<StatCard
icon={<FireIcon className="h-5 w-5 text-[#0F766E]" />}
label="Punkte gesamt"
value={profile.points.total.toLocaleString("de-DE")}
/>
<StatCard
icon={<FireIcon className="h-5 w-5 text-[#0F766E]" />}
label="Punkte (30 Tage)"
value={profile.points.last30Days.toLocaleString("de-DE")}
/>
<StatCard
icon={<SparklesIcon className="h-5 w-5 text-[#0F766E]" />}
label="Aktive Tage (7 Tage)"
value={`${profile.activeDaysLast7}/7`}
/>
</div>
<p className="px-1 text-xs text-gray-400">
Gesundheitsdaten wie Gewicht, Kalorien und Ziele sind immer privat und
werden hier nie angezeigt.
</p>
</div>
</DataPageLayout>
);
}
function StatCard({
icon,
label,
value,
}: {
icon: React.ReactNode;
label: string;
value: string;
}) {
return (
<div className="rounded-xl border border-gray-200 bg-white p-4">
<div className="flex items-center gap-2 text-xs font-medium text-gray-500">
{icon}
{label}
</div>
<p className="mt-2 text-2xl font-bold text-gray-900">{value}</p>
</div>
);
}