Skip to content

Commit 3512624

Browse files
author
Rajat
committed
Untested: Coding done; accomplishment page; cert pdf generation; cert template management
1 parent 4f33e53 commit 3512624

20 files changed

Lines changed: 1700 additions & 327 deletions

File tree

Lines changed: 165 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,179 @@
11
"use client";
22

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";
511
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";
718

819
export default function AccomplishmentPage() {
920
const params = useParams();
1021
const certId = params.certId;
1122
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+
}
12113

13114
return (
14115
<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>
19177
</Section>
20178
);
21179
}

0 commit comments

Comments
 (0)