feat: implement certificate download functionality with image and PDF options

This commit is contained in:
Kemas M. Aryadary Rasyad
2026-06-30 13:46:34 +07:00
parent f22e4683a7
commit 7fffc08912
3 changed files with 119 additions and 14 deletions
@@ -52,16 +52,16 @@ export default function SertificateList() {
{projects.map((item: Project) => (
<div
key={item.id}
className="bg-white border border-gray-200 rounded-xl overflow-hidden shadow-sm hover:shadow-md transition-shadow cursor-pointer group"
className="bg-white border border-card-outline rounded-xl overflow-hidden shadow-sm hover:shadow-md transition-shadow cursor-pointer group"
onClick={() => navigate(`/intern/certificates/${item.slug}`, { state: { project: item } })}
>
{/* Thumbnail */}
<div className="p-6 pb-0">
<div className="rounded-lg overflow-hidden">
<div className="w-full h-32 bg-gradient-to-br from-red-400 to-red-600 flex items-center justify-center">
<div className="w-full h-32 bg-gray-300 flex items-center justify-center">
{(() => {
const Icon = getDynamicIcon(item.project_icon);
return <Icon className="w-10 h-10 text-white" />;
return <Icon className="w-10 h-10 text-primary" />;
})()}
</div>
</div>
@@ -1,8 +1,9 @@
// CertificateAvailable.tsx
import { useState } from "react";
import { useState, useRef } from "react";
import { Download, Link, Printer } from "lucide-react";
import type { ProjectDetail } from "../../../../types/project.types";
import type { Certificate } from "../../../../types/certificate.types";
import { downloadCertificateAsImage, downloadCertificateAsPdf } from "../../../utils/DownloadImagetoDevice";
interface CertificateAvailableProps {
project: ProjectDetail | null;
@@ -11,6 +12,9 @@ interface CertificateAvailableProps {
export default function CertificateAvailable({ project, certificate }: CertificateAvailableProps) {
const [copied, setCopied] = useState(false);
const [downloadingImg, setDownloadingImg] = useState(false);
const [downloadingPdf, setDownloadingPdf] = useState(false);
const certificateRef = useRef<HTMLDivElement>(null);
const handleCopyLink = () => {
navigator.clipboard.writeText(window.location.href);
@@ -33,12 +37,24 @@ export default function CertificateAvailable({ project, certificate }: Certifica
})()
: "-";
const handleDownload = () => {
const a = document.createElement("a");
a.href = certificate.image_path;
a.download = `Sertifikat - ${certificate.certificate_no}.png`;
a.target = "_blank";
a.click();
const handleDownloadImage = async () => {
setDownloadingImg(true);
await downloadCertificateAsImage(
certificateRef.current,
`Sertifikat - ${certificate.certificate_no}`,
"png",
certificate.image_path
);
setDownloadingImg(false);
};
const handleDownloadPdf = async () => {
setDownloadingPdf(true);
await downloadCertificateAsPdf(
certificateRef.current,
`Sertifikat - ${certificate.certificate_no}`
);
setDownloadingPdf(false);
};
return (
@@ -87,11 +103,12 @@ export default function CertificateAvailable({ project, certificate }: Certifica
<div className="flex flex-col gap-3">
{/* Unduh Sertifikat */}
<button
onClick={handleDownload}
onClick={handleDownloadPdf}
disabled={downloadingPdf}
className="flex items-center justify-center gap-2 w-full bg-red-700 hover:bg-red-800 active:scale-95 transition-all text-white font-semibold py-2 rounded-xl text-sm"
>
<Download className="w-4 h-4" />
Unduh Sertifikat
{downloadingPdf? "Memproses": "Unduh Sertifikat"}
</button>
{/* Bagikan ke LinkedIn */}
@@ -101,9 +118,12 @@ export default function CertificateAvailable({ project, certificate }: Certifica
{/* Cetak & Salin Link */}
<div className="grid grid-cols-2 gap-3">
<button className="flex flex-col items-center justify-center gap-1.5 border border-card-outline hover:bg-gray-50 active:scale-95 transition-all text-red-700 font-medium py-3.5 rounded-xl text-sm">
<button
onClick={handleDownloadImage}
disabled={downloadingImg}
className="flex flex-col items-center justify-center gap-1.5 border border-card-outline hover:bg-gray-50 active:scale-95 transition-all text-red-700 font-medium py-3.5 rounded-xl text-sm">
<Printer className="w-5 h-5" />
Cetak
{downloadingImg? "Memproses" : "Cetak"}
</button>
<button
onClick={handleCopyLink}