Files
pargar/frontend/src/app/c/[id]/page.tsx
T
ahp 03610bc3d3 feat: implement 8 features across full stack
- Leaderboard: rank-based percentile tiers (champion/gold/silver/bronze),
    deterministic tie-breaking (xp DESC, user_id ASC)
  - Mentor chat: md: breakpoints, safe-area-inset-bottom padding
  - Learning progress: server-side progressPct, curriculum expansion safety
  - Physical orders: hard-disabled with 410 Gone
  - Certificate approval: pending/issued flow, admin issue/revoke endpoints,
    certificate_requires_approval setting (migration 0031)
  - Draft/publish: is_published on lessons and chapters, admin publish UI
    (migration 0030)
  - Video panel: static thumbnails (no preload), client-side pagination
  - Rich editor: TipTap + tiptap-markdown for lesson descriptions,
    marked + DOMPurify for SafeMarkdown rendering
2026-10-18 10:14:22 +03:30

74 lines
2.1 KiB
TypeScript

"use client";
import { useQuery } from "@tanstack/react-query";
import { useParams } from "next/navigation";
import { Loader2 } from "lucide-react";
import { CertificateCard } from "@/components/certificate-card";
import { NotFoundState, notFoundSentence } from "@/components/not-found-state";
import { http, toUserError } from "@/lib/api";
import { useAuth } from "@/lib/auth-store";
import type { Certificate } from "@/lib/types";
type PublicCertResponse = {
certificate: { publicId: string; fullName: string; issuedAt: string };
course: string;
};
export default function CertificatePage() {
const params = useParams<{ id: string }>();
const publicId = params?.id ?? "";
const authUser = useAuth((s) => s.user);
const { data, isLoading, error } = useQuery({
queryKey: ["certificate", publicId],
queryFn: () => http.get<PublicCertResponse>(`/api/certificates/${publicId}`),
enabled: !!publicId,
meta: { silentError: true },
});
const { data: mine } = useQuery({
queryKey: ["me", "certificate"],
queryFn: () => http.get<{ certificate: Certificate | null }>("/api/me/certificate"),
enabled: !!authUser,
});
const isOwner = !!authUser && mine?.certificate?.publicId === publicId;
if (isLoading) {
return (
<div className="flex min-h-[60vh] items-center justify-center">
<Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />
</div>
);
}
if (error || !data?.certificate) {
return (
<NotFoundState
title="گواهینامه پیدا نشد"
description={notFoundSentence(
error ? toUserError(error, "") : "",
"این گواهینامه در سامانه نیست.",
"گواهینامه پیدا نشد",
)}
href="/"
actionLabel="بازگشت به خانه"
/>
);
}
const cert = data.certificate;
return (
<CertificateCard
cert={{
publicId: cert.publicId,
fullName: cert.fullName,
issuedAt: cert.issuedAt,
course: data.course,
}}
showOwnerLinks={isOwner}
/>
);
}