|
1 | 1 | "use client"; |
2 | 2 |
|
3 | | -import { Section } from "@courselit/page-primitives"; |
4 | | -import { useParams } from "next/navigation"; |
| 3 | +import { |
| 4 | + Badge, |
| 5 | + Header1, |
| 6 | + Header2, |
| 7 | + Section, |
| 8 | + Text1, |
| 9 | +} from "@courselit/page-primitives"; |
| 10 | +import { redirect, useParams } from "next/navigation"; |
5 | 11 | import { ThemeContext } from "@components/contexts"; |
6 | | -import { useContext } from "react"; |
| 12 | +import { useContext, useEffect, useState } from "react"; |
| 13 | +import { BadgeCheck, ExternalLinkIcon } from "lucide-react"; |
| 14 | +import Link from "next/link"; |
| 15 | +import { useGraphQLFetch } from "@/hooks/use-graphql-fetch"; |
| 16 | +import { Image } from "@courselit/components-library"; |
| 17 | +import { formattedLocaleDate } from "@ui-lib/utils"; |
7 | 18 |
|
8 | 19 | export default function AccomplishmentPage() { |
9 | 20 | const params = useParams(); |
10 | 21 | const certId = params.certId; |
11 | 22 | const { theme } = useContext(ThemeContext); |
| 23 | + const [certificate, setCertificate] = useState<any>(null); |
| 24 | + const [isIframeLoaded, setIsIframeLoaded] = useState(false); |
| 25 | + const fetch = useGraphQLFetch(); |
| 26 | + |
| 27 | + useEffect(() => { |
| 28 | + async function getCertificate() { |
| 29 | + const query = ` |
| 30 | + query GetCertificate($certificateId: String!) { |
| 31 | + certificate: getCertificate(certificateId: $certificateId) { |
| 32 | + certificateId |
| 33 | + title |
| 34 | + subtitle |
| 35 | + description |
| 36 | + signatureImage { |
| 37 | + mediaId |
| 38 | + file |
| 39 | + thumbnail |
| 40 | + } |
| 41 | + signatureName |
| 42 | + signatureDesignation |
| 43 | + logo { |
| 44 | + mediaId |
| 45 | + file |
| 46 | + thumbnail |
| 47 | + } |
| 48 | + productTitle |
| 49 | + userName |
| 50 | + createdAt |
| 51 | + userImage { |
| 52 | + mediaId |
| 53 | + file |
| 54 | + thumbnail |
| 55 | + } |
| 56 | + productPageId |
| 57 | + } |
| 58 | + } |
| 59 | + `; |
| 60 | + const fetchRequest = fetch |
| 61 | + .setPayload({ |
| 62 | + query, |
| 63 | + variables: { |
| 64 | + certificateId: certId, |
| 65 | + }, |
| 66 | + }) |
| 67 | + .build(); |
| 68 | + |
| 69 | + try { |
| 70 | + const response = await fetchRequest.exec(); |
| 71 | + if (response.certificate) { |
| 72 | + setCertificate(response.certificate); |
| 73 | + } else { |
| 74 | + redirect("/"); |
| 75 | + } |
| 76 | + } catch (error) { |
| 77 | + redirect("/"); |
| 78 | + } |
| 79 | + } |
| 80 | + |
| 81 | + if (certId) { |
| 82 | + getCertificate(); |
| 83 | + } |
| 84 | + }, [certId]); |
| 85 | + |
| 86 | + if (!certificate) { |
| 87 | + return ( |
| 88 | + <Section theme={theme.theme}> |
| 89 | + <div className="flex flex-col gap-8 animate-pulse"> |
| 90 | + <div className="flex flex-col sm:flex-row gap-2 items-center"> |
| 91 | + <div className="h-7 sm:h-9 w-48 sm:w-80 rounded bg-gray-200 dark:bg-gray-700" /> |
| 92 | + <div className="h-6 w-20 rounded bg-gray-200 dark:bg-gray-700" /> |
| 93 | + </div> |
| 94 | + |
| 95 | + {/* User completion info skeleton */} |
| 96 | + <div className="flex flex-col sm:flex-row gap-3 sm:gap-4"> |
| 97 | + <div className="h-24 w-24 sm:h-32 sm:w-32 md:h-40 md:w-40 rounded-full bg-gray-200 dark:bg-gray-700 mx-auto sm:mx-0" /> |
| 98 | + <div className="flex flex-col gap-2 text-center sm:text-left w-full"> |
| 99 | + <div className="h-5 w-64 sm:w-80 rounded bg-gray-200 dark:bg-gray-700 mx-auto sm:mx-0" /> |
| 100 | + <div className="h-4 w-40 rounded bg-gray-200 dark:bg-gray-700 mx-auto sm:mx-0" /> |
| 101 | + </div> |
| 102 | + </div> |
| 103 | + |
| 104 | + {/* Certificate section header + viewer skeleton */} |
| 105 | + <div className="flex flex-col gap-4"> |
| 106 | + <div className="h-6 w-32 rounded bg-gray-200 dark:bg-gray-700" /> |
| 107 | + <div className="w-full lg:h-[900px] h-[300px] md:h-[600px] rounded-lg bg-gray-200 dark:bg-gray-700" /> |
| 108 | + </div> |
| 109 | + </div> |
| 110 | + </Section> |
| 111 | + ); |
| 112 | + } |
12 | 113 |
|
13 | 114 | return ( |
14 | 115 | <Section theme={theme.theme}> |
15 | | - <iframe |
16 | | - src={`/certificate/internal/${certId}`} |
17 | | - className="w-full h-[900px]" |
18 | | - /> |
| 116 | + <div className="flex flex-col gap-8"> |
| 117 | + <div className="flex flex-col sm:flex-row gap-2 items-center"> |
| 118 | + <Header1 theme={theme.theme}> |
| 119 | + {certificate.productTitle} |
| 120 | + </Header1> |
| 121 | + <div> |
| 122 | + <Link href={`/p/${certificate.productPageId}`}> |
| 123 | + <Badge |
| 124 | + theme={theme.theme} |
| 125 | + className="flex items-center gap-1" |
| 126 | + > |
| 127 | + Visit <ExternalLinkIcon className="h-4 w-4" /> |
| 128 | + </Badge> |
| 129 | + </Link> |
| 130 | + </div> |
| 131 | + </div> |
| 132 | + |
| 133 | + {/* User completion info - responsive layout */} |
| 134 | + <div className="flex flex-col sm:flex-row gap-3 sm:gap-4"> |
| 135 | + {certificate.userImage && ( |
| 136 | + <div className="h-24 w-24 sm:h-32 sm:w-32 md:h-40 md:w-40 rounded-full overflow-hidden flex-shrink-0 mx-auto sm:mx-0"> |
| 137 | + <Image |
| 138 | + src={certificate.userImage.file} |
| 139 | + alt={certificate.userName} |
| 140 | + className="w-full h-full object-cover" |
| 141 | + /> |
| 142 | + </div> |
| 143 | + )} |
| 144 | + <div className="flex flex-col gap-2 text-center sm:text-left"> |
| 145 | + <Text1 |
| 146 | + theme={theme.theme} |
| 147 | + className="flex items-center justify-center gap-2" |
| 148 | + > |
| 149 | + <BadgeCheck className="h-4 w-4 sm:h-5 sm:w-5" /> |
| 150 | + Completed by{" "} |
| 151 | + <span className="font-bold"> |
| 152 | + {certificate.userName} |
| 153 | + </span> |
| 154 | + </Text1> |
| 155 | + <Text1 theme={theme.theme} className="text-gray-500"> |
| 156 | + {formattedLocaleDate(+certificate.createdAt)} |
| 157 | + </Text1> |
| 158 | + </div> |
| 159 | + </div> |
| 160 | + |
| 161 | + {/* Certificate section header - responsive */} |
| 162 | + <div className="flex flex-col gap-4"> |
| 163 | + <Header2 theme={theme.theme}>Certificate</Header2> |
| 164 | + |
| 165 | + <div className="relative w-full"> |
| 166 | + {!isIframeLoaded && ( |
| 167 | + <div className="w-full lg:h-[900px] h-[300px] md:h-[600px] rounded-lg bg-gray-200 dark:bg-gray-700 animate-pulse" /> |
| 168 | + )} |
| 169 | + <iframe |
| 170 | + className={`w-full lg:h-[900px] h-[300px] md:h-[600px] transition-opacity duration-300 ${isIframeLoaded ? "opacity-100" : "opacity-0"}`} |
| 171 | + src={`/api/certificate/${certId}`} |
| 172 | + onLoad={() => setIsIframeLoaded(true)} |
| 173 | + ></iframe> |
| 174 | + </div> |
| 175 | + </div> |
| 176 | + </div> |
19 | 177 | </Section> |
20 | 178 | ); |
21 | 179 | } |
0 commit comments