Skip to content

Commit 2a4ef9d

Browse files
committed
Merge master into dev - certificate fixes and kanban improvements
2 parents 9796e00 + dcf8b9d commit 2a4ef9d

4 files changed

Lines changed: 204 additions & 129 deletions

File tree

‎apps/codebility/app/home/(dashboard)/_components/DashboardCertificate.tsx‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ const DashboardCertificate = () => {
99
const router = useRouter();
1010
const { user, userLevel } = useUserStore();
1111

12+
// Show certificate button if user is admin (role_id 1) or has points/level requirements
13+
const canViewCertificate = user?.role_id === 1 || user; // Admins can always view, others need to be logged in
14+
15+
if (!canViewCertificate) {
16+
return null;
17+
}
18+
1219
return (
1320
<div className="absolute left-4 top-4 z-10">
1421
<Tooltip>
@@ -29,7 +36,7 @@ const DashboardCertificate = () => {
2936
</Button>
3037
</TooltipTrigger>
3138
<TooltipContent side="bottom" align="start">
32-
View your certificate
39+
{user?.role_id === 1 ? "View certificates (Admin)" : "View your certificate"}
3340
</TooltipContent>
3441
</Tooltip>
3542
</div>

‎apps/codebility/app/home/(dashboard)/_components/DashboardDownloadCertificate.tsx‎

Lines changed: 95 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -3,38 +3,9 @@
33
import type { ReactNode, RefObject } from "react";
44
import React, { forwardRef } from "react";
55
import Image from "next/image";
6-
import html2canvas from "html2canvas";
7-
import jsPDF from "jspdf";
6+
// Removed imports for PDF functionality
87

9-
export const handleDownload = async (
10-
certRef: RefObject<HTMLDivElement | null>,
11-
name: string,
12-
) => {
13-
if (!certRef.current) return;
14-
15-
// Wait a bit to ensure images are loaded
16-
await new Promise(resolve => setTimeout(resolve, 500));
17-
18-
const canvas = await html2canvas(certRef.current, {
19-
useCORS: true,
20-
scale: 2,
21-
backgroundColor: null,
22-
logging: true, // Enable logging to debug
23-
allowTaint: true, // Allow tainted images
24-
foreignObjectRendering: true, // Better SVG/CSS rendering
25-
});
26-
27-
const imgData = canvas.toDataURL("image/png");
28-
29-
const pdf = new jsPDF({
30-
orientation: "landscape",
31-
unit: "px",
32-
format: [canvas.width, canvas.height],
33-
});
34-
35-
pdf.addImage(imgData, "PNG", 0, 0, canvas.width, canvas.height);
36-
pdf.save(`${name}-certificate.pdf`);
37-
};
8+
// Removed PDF download functionality - keeping only PNG export
389

3910
export interface CertificateProps {
4011
title: string;
@@ -47,73 +18,112 @@ const Certificate = forwardRef<HTMLDivElement, CertificateProps>(
4718
(props, ref) => {
4819
const { title, name, mainSentence, description1 } = props;
4920

50-
const background = "/assets/images/bg-certificate.png";
51-
const logo = "/assets/images/codebility.png";
52-
const signature = "/assets/images/signature1.png";
21+
// Use relative paths - Next.js serves from public folder
22+
const logo = '/assets/images/codebility.png';
23+
const signature = '/assets/images/signature1.png';
5324

5425
return (
5526
<div
5627
ref={ref}
57-
className="relative h-[700px] w-[1000px] overflow-hidden font-sans text-white"
28+
className="relative h-[700px] w-[1000px] overflow-hidden"
5829
style={{
59-
backgroundImage: `url(${background})`,
60-
backgroundSize: "cover",
61-
backgroundPosition: "center",
62-
backgroundColor: "transparent",
30+
backgroundColor: "#0a0a1a",
31+
backgroundImage: `
32+
radial-gradient(circle at 20% 50%, rgba(120, 119, 198, 0.1) 0%, transparent 50%),
33+
radial-gradient(circle at 80% 50%, rgba(255, 119, 198, 0.1) 0%, transparent 50%),
34+
radial-gradient(circle at 50% 100%, rgba(120, 198, 255, 0.1) 0%, transparent 50%),
35+
linear-gradient(180deg, rgba(255,255,255,0.02) 0%, transparent 100%)
36+
`,
37+
fontFamily: "'Arial', sans-serif",
38+
position: "relative",
6339
}}
6440
>
65-
<div className="flex h-full w-full flex-col justify-between px-40 py-10 text-center">
66-
<div>
67-
<div className="mx-auto w-60 h-[80px] relative">
68-
<img className="mx-auto w-60" src={logo} alt="logo" />
41+
{/* Background pattern - simple dots for better PDF rendering */}
42+
<svg className="absolute inset-0 w-full h-full" style={{ opacity: 0.05 }}>
43+
<defs>
44+
<pattern id="dots" x="0" y="0" width="40" height="40" patternUnits="userSpaceOnUse">
45+
<circle cx="2" cy="2" r="1" fill="white" />
46+
<circle cx="22" cy="22" r="1" fill="white" />
47+
<circle cx="2" cy="22" r="1" fill="white" />
48+
<circle cx="22" cy="2" r="1" fill="white" />
49+
</pattern>
50+
</defs>
51+
<rect width="100%" height="100%" fill="url(#dots)" />
52+
</svg>
53+
54+
{/* Decorative border with glow effect */}
55+
<div
56+
className="absolute rounded-lg"
57+
style={{
58+
top: "40px",
59+
left: "40px",
60+
right: "40px",
61+
bottom: "40px",
62+
border: "2px solid rgba(255,255,255,0.3)",
63+
boxShadow: "inset 0 0 20px rgba(255,255,255,0.1), 0 0 20px rgba(120, 119, 198, 0.3)"
64+
}}
65+
/>
66+
<div
67+
className="absolute rounded-lg"
68+
style={{
69+
top: "50px",
70+
left: "50px",
71+
right: "50px",
72+
bottom: "50px",
73+
border: "1px solid rgba(255,255,255,0.2)",
74+
}}
75+
/>
76+
77+
{/* Corner decorations */}
78+
<div className="absolute w-16 h-16 border-t-2 border-l-2 border-white/30 rounded-tl-lg" style={{ top: "60px", left: "60px" }} />
79+
<div className="absolute w-16 h-16 border-t-2 border-r-2 border-white/30 rounded-tr-lg" style={{ top: "60px", right: "60px" }} />
80+
<div className="absolute w-16 h-16 border-b-2 border-l-2 border-white/30 rounded-bl-lg" style={{ bottom: "60px", left: "60px" }} />
81+
<div className="absolute w-16 h-16 border-b-2 border-r-2 border-white/30 rounded-br-lg" style={{ bottom: "60px", right: "60px" }} />
82+
83+
<div className="relative h-full w-full px-40 pt-20 pb-24 text-center" style={{ color: "white", zIndex: 1 }}>
84+
{/* Top section with logo and title */}
85+
<div className="mb-6">
86+
<div className="mx-auto w-48 h-[60px] relative mb-3">
87+
<img
88+
className="mx-auto w-48 h-auto"
89+
src={logo}
90+
alt="logo"
91+
onError={(e) => console.error('Failed to load logo:', e)}
92+
style={{ maxHeight: "60px", objectFit: "contain" }}
93+
/>
6994
</div>
70-
<h1 className="mt-3 leading-tight">
71-
<span className="my-5 text-5xl font-bold">CERTIFICATE</span>
95+
<h1 className="leading-tight" style={{ color: "white" }}>
96+
<span className="text-5xl font-bold" style={{ color: "white" }}>CERTIFICATE</span>
7297
</h1>
73-
<h1>
74-
<span className="text-3xl">OF {title}</span>
98+
<h1 className="mt-2" style={{ color: "white" }}>
99+
<span className="text-3xl" style={{ color: "white", letterSpacing: "0.05em" }}>OF {title}</span>
75100
</h1>
76-
<p className="mt-1 text-base">This certifies that</p>
77-
<h2 className="mt-1 text-3xl font-semibold">
101+
</div>
102+
103+
{/* Middle section with content - better spacing */}
104+
<div className="mb-8 px-12">
105+
<p className="text-base mb-2" style={{ color: "white", letterSpacing: "0.02em" }}>This certifies that</p>
106+
<h2 className="text-3xl font-semibold mb-3" style={{ color: "white", letterSpacing: "0.08em", wordSpacing: "0.2em" }}>
78107
{name.toUpperCase()}
79108
</h2>
80-
<hr className="mx-20 my-3 border-t border-white" />
81-
{mainSentence}
82-
<br />
83-
{description1}
84-
<br />
85-
<br />
86-
<br />
87-
<p>Authorized by:</p>
109+
<hr className="mx-20 my-4 border-t border-white" style={{ borderColor: "white" }} />
110+
<div className="mb-4 leading-relaxed" style={{ color: "white", letterSpacing: "0.02em", lineHeight: "1.6" }}>{mainSentence}</div>
111+
<div className="leading-relaxed" style={{ color: "white", letterSpacing: "0.02em", lineHeight: "1.6" }}>{description1}</div>
88112
</div>
89-
90-
{/* Projects */}
91-
{/* <div className="mt-3">
92-
<h3 className="mb-2 text-base font-semibold">
93-
Key Projects Completed:
94-
</h3>
95-
<ul className="mx-auto grid grid-cols-2 gap-x-8 gap-y-3 px-20 text-left text-sm">
96-
{key_projects.map((proj, idx) => (
97-
<li
98-
key={idx}
99-
className="flex items-start gap-2 leading-relaxed"
100-
>
101-
<span className="mt-[2px] text-base text-white">•</span>
102-
<span className="underline">{proj}</span>
103-
</li>
104-
))}
105-
</ul>
106-
</div> */}
107-
108-
{/* Signature */}
109-
<div className="mt-10">
110-
<div className="relative mx-auto w-fit">
111-
<img
112-
src={signature}
113-
alt="signature"
114-
className="pointer-events-none mx-auto w-60 opacity-90"
115-
/>
116-
</div>
113+
114+
{/* Bottom section with signature - moved up significantly */}
115+
<div className="mt-8">
116+
<p className="mb-2" style={{ color: "white", letterSpacing: "0.05em" }}>Authorized by:</p>
117+
<img
118+
src={signature}
119+
alt="signature"
120+
className="pointer-events-none mx-auto opacity-90"
121+
onError={(e) => console.error('Failed to load signature:', e)}
122+
style={{ maxHeight: "50px", width: "200px", objectFit: "contain" }}
123+
/>
124+
<p className="mt-1 text-sm" style={{ color: "rgba(255,255,255,0.8)", letterSpacing: "0.03em" }}>
125+
CEO & Founder, Codebility
126+
</p>
117127
</div>
118128
</div>
119129
</div>

0 commit comments

Comments
 (0)