diff --git a/src/Pages/internify/intern/components/CertificateAvailabe.tsx b/src/Pages/internify/intern/components/CertificateAvailabe.tsx
index 70757f6..cbda7df 100644
--- a/src/Pages/internify/intern/components/CertificateAvailabe.tsx
+++ b/src/Pages/internify/intern/components/CertificateAvailabe.tsx
@@ -1,13 +1,15 @@
+// CertificateAvailable.tsx
import { useState } from "react";
import { Download, Link, Printer } from "lucide-react";
import type { ProjectDetail } from "../../../../types/project.types";
-import certificateImg from "../../../../assets/certificate.png";
+import type { Certificate } from "../../../../types/certificate.types";
interface CertificateAvailableProps {
project: ProjectDetail | null;
+ certificate: Certificate;
}
-export default function CertificateAvailable({ project }: CertificateAvailableProps) {
+export default function CertificateAvailable({ project, certificate }: CertificateAvailableProps) {
const [copied, setCopied] = useState(false);
const handleCopyLink = () => {
@@ -31,80 +33,89 @@ export default function CertificateAvailable({ project }: CertificateAvailablePr
})()
: "-";
+ const handleDownload = () => {
+ const a = document.createElement("a");
+ a.href = certificate.image_path;
+ a.download = `Sertifikat - ${certificate.certificate_no}.png`;
+ a.target = "_blank";
+ a.click();
+ };
+
return (
{/* Certificate Card */}
-
-

-
+
+

+
- {/* Program Summary */}
-
-
-
-
Ringkasan Program
-
-
- {[
- { label: "Nama Program", value: project?.project_name ?? "-" },
- { label: "Durasi", value: duration },
- { label: "Total Tugas", value: project ? `${project.total_tasks} Tugas` : "-" },
- {
- label: "Status",
- value: (
-
- UNVERIFIED
-
- ),
- },
- ].map((row) => (
-
- {row.label}
- {row.value}
-
- ))}
-
+ {/* Program Summary */}
+
+
+
+
Ringkasan Program
-
-
-
-
- {/* Unduh Sertifikat */}
-
-
- {/* Bagikan ke LinkedIn */}
-
-
- {/* Cetak & Salin Link */}
-
-
-
+
+ {[
+ { label: "Nama Program", value: project?.project_name ?? "-" },
+ { label: "Durasi", value: duration },
+ { label: "Total Tugas", value: project ? `${project.total_tasks} Tugas` : "-" },
+ {
+ label: "Status",
+ value: (
+
+ {certificate.certificate_status.toUpperCase()}
+
+ ),
+ },
+ ].map((row) => (
+
+ {row.label}
+ {row.value}
+ ))}
+
+
+
+
+ {/* Unduh Sertifikat */}
+
+
+ {/* Bagikan ke LinkedIn */}
+
+
+ {/* Cetak & Salin Link */}
+
+
+
+
);
}
\ No newline at end of file
diff --git a/src/Pages/internify/intern/components/CertificateHistory.tsx b/src/Pages/internify/intern/components/CertificateHistory.tsx
index 55b37e5..62c5676 100644
--- a/src/Pages/internify/intern/components/CertificateHistory.tsx
+++ b/src/Pages/internify/intern/components/CertificateHistory.tsx
@@ -1,8 +1,8 @@
import { useNavigate } from "react-router-dom";
import { History, Loader2, AlertCircle } from "lucide-react";
import type { Certificate } from "../../../../types/certificate.types";
-import certificateImg from "../../../../assets/certificate.png";
-import { useMyCertificates } from "../../../../hooks/useSertificates";
+import certificateImg from "../../../../assets/certificate.png"; //Placeholder
+import { useMyCertificates } from "../../../../hooks/useCertificates";
export default function SertificateHistory() {
const navigate = useNavigate();
diff --git a/src/Pages/internify/intern/components/CertificateNotAvailable.tsx b/src/Pages/internify/intern/components/CertificateNotAvailable.tsx
index 97eeab3..6a0190e 100644
--- a/src/Pages/internify/intern/components/CertificateNotAvailable.tsx
+++ b/src/Pages/internify/intern/components/CertificateNotAvailable.tsx
@@ -1,27 +1,23 @@
+// CertificateNotAvailable.tsx
import { Clock, Lock, Award } from "lucide-react";
-import { useClaimCertificate } from "../../../../hooks/useSertificates";
interface CertificateNotAvailableProps {
progress: number;
remainingTasks: number;
allTasksDone?: boolean;
- project_id?: number;
+ onClaim: () => void;
+ claiming: boolean;
+ claimError: string | null;
}
export default function CertificateNotAvailable({
progress,
remainingTasks,
allTasksDone = false,
- project_id
+ onClaim,
+ claiming,
+ claimError,
}: CertificateNotAvailableProps) {
-
- const { claim, loading, error } = useClaimCertificate();
-
- const handleClaim = async () => {
- if (!project_id) return;
- await claim(project_id);
- };
-
return (
{/* Icon */}
@@ -60,12 +56,13 @@ export default function CertificateNotAvailable({
Semua tugas telah diselesaikan! Silahkan tunggu untuk mendapatkan sertifikat
+ {claimError &&
{claimError}
}
>
) : (
<>
@@ -91,7 +88,6 @@ export default function CertificateNotAvailable({
/>
- {error &&
{error}
}
>
)}
diff --git a/src/Pages/internify/mentor/CertificateForm.tsx b/src/Pages/internify/mentor/CertificateForm.tsx
index 32138b4..61ee764 100644
--- a/src/Pages/internify/mentor/CertificateForm.tsx
+++ b/src/Pages/internify/mentor/CertificateForm.tsx
@@ -1,7 +1,7 @@
import { useRef, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { CalendarDays, UploadCloud, CheckCircle2, XCircle, Loader2 } from "lucide-react";
-import { useUploadCertificateTemplate } from "../../../hooks/useSertificates";
+import { useUploadCertificateTemplate } from "../../../hooks/useCertificates";
import { useProjectDetail } from "../../../hooks/useProjects";
import EligibleInternTable from "./components/EligibleInternTable";
import { useProjectInternProgress } from "../../../hooks/useInternProgress"
@@ -68,7 +68,7 @@ export default function CertificateDetail() {
- {/* ── Left Column ── */}
+ {/* Left Column */}
{project.project_name}
@@ -76,11 +76,9 @@ export default function CertificateDetail() {
{new Date(project.start_date).toLocaleDateString("en-US", { month: "short", year: "numeric" })}
- {" — "}
+ {" - "}
{new Date(project.end_date).toLocaleDateString("en-US", { month: "short", year: "numeric" })}
-
- {/* ── Upload Template ── */}
- {/* ── Stats ── */}
+ {/* Stats: Total Intern dalam project dan jumlah intern eligible */}
{project.total_members}
@@ -176,11 +174,12 @@ export default function CertificateDetail() {
- {/* ── Right Column ── */}
+ {/* Right Column */}
diff --git a/src/Pages/internify/mentor/CertificateResult.tsx b/src/Pages/internify/mentor/CertificateResult.tsx
index 4398e81..2cabc93 100644
--- a/src/Pages/internify/mentor/CertificateResult.tsx
+++ b/src/Pages/internify/mentor/CertificateResult.tsx
@@ -10,61 +10,109 @@ import {
ChevronLeft,
ChevronRight,
} from "lucide-react";
+import { useProjectCertificates } from "../../../hooks/useCertificates";
+import type { Certificate } from "../../../types/certificate.types";
+import { useParams } from "react-router-dom";
-// ─── Types ────────────────────────────────────────────────────────────────────
-interface InternCert {
- id: number;
- name: string;
- role: string;
- initials: string;
- credentialId: string;
-}
+const PER_PAGE = 5;
-// ─── Mock Data ────────────────────────────────────────────────────────────────
-const mockCerts: InternCert[] = [
- { id: 1, name: "Alex Johnson", role: "Backend Engineering", initials: "AJ", credentialId: "CERT-2023-0045" },
- { id: 2, name: "Sarah Lin", role: "UI/UX Design Specialist",initials: "SL", credentialId: "CERT-2023-0046" },
- { id: 3, name: "Marcus Kane", role: "Data Analytics", initials: "MK", credentialId: "CERT-2023-0047" },
- { id: 4, name: "Priya Sharma", role: "Frontend Engineering", initials: "PS", credentialId: "CERT-2023-0048" },
- { id: 5, name: "Derek Lim", role: "DevOps & Cloud", initials: "DL", credentialId: "CERT-2023-0049" },
- { id: 6, name: "Nina Castillo", role: "Mobile Development", initials: "NC", credentialId: "CERT-2023-0050" },
-];
-const TOTAL_CERTS = 18;
-const PER_PAGE = 3;
-
-// ─── Avatar ───────────────────────────────────────────────────────────────────
-const avatarColors: Record
= {
- AJ: "bg-blue-100 text-blue-700",
- SL: "bg-purple-100 text-purple-700",
- MK: "bg-orange-100 text-orange-700",
- PS: "bg-teal-100 text-teal-700",
- DL: "bg-green-100 text-green-700",
- NC: "bg-pink-100 text-pink-700",
+const getInitials = (name: string): string => {
+ const parts = name.trim().split(/\s+/);
+ if (parts.length === 1) return parts[0][0].toUpperCase();
+ return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
};
-function Avatar({ initials }: { initials: string }) {
- const color = avatarColors[initials] ?? "bg-gray-100 text-gray-600";
+function Avatar({ name }: { name: string }) {
+ // "Kemas M Aryadary Rasyad" → "KR"
+ const color = "bg-gray-100 text-gray-600";
return (
- {initials}
+ {getInitials(name)}
+
+ );
+}
+
+function CertificateResultSkeleton() {
+ return (
+
+ {/* Hero */}
+
+
+ {/* Table */}
+
+ {/* Header */}
+
+
+ {/* Column headers */}
+
+
+ {/* Rows */}
+ {Array.from({ length: PER_PAGE }).map((_, i) => (
+
+
+
+
+ {Array.from({ length: 3 }).map((_, j) => (
+
+ ))}
+
+
+ ))}
+
+ {/* Footer */}
+
+
+
+ {Array.from({ length: 3 }).map((_, i) => (
+
+ ))}
+
+
+
);
}
// ─── Main Page ────────────────────────────────────────────────────────────────
export default function CertificateResult() {
- const [search, setSearch] = useState("");
- const [page, setPage] = useState(1);
+ const { id } = useParams<{ id: string }>();
+ const [search, setSearch] = useState("");
+ const [page, setPage] = useState(1);
+ const {certificates, loading, error} = useProjectCertificates(Number(id));
+
+ if (loading) return ;
+ if (error) return Gagal memuat data sertifikat.
;
// Filter + paginate
- const filtered = mockCerts.filter(
+ const filtered = certificates.filter(
(c) =>
- c.name.toLowerCase().includes(search.toLowerCase()) ||
- c.role.toLowerCase().includes(search.toLowerCase())
+ c.user.full_name.toLowerCase().includes(search.toLowerCase()) ||
+ c.user.email.toLowerCase().includes(search.toLowerCase())
);
const totalPages = Math.ceil(filtered.length / PER_PAGE);
- const paginated = filtered.slice((page - 1) * PER_PAGE, page * PER_PAGE);
+ const paginated = filtered.slice((page - 1) * PER_PAGE, page * PER_PAGE);
const handleSearch = (e: React.ChangeEvent) => {
setSearch(e.target.value);
@@ -90,7 +138,7 @@ export default function CertificateResult() {
{/* Headline */}
- {TOTAL_CERTS} Certificates Successfully Generated!
+ {certificates.length} Certificates Successfully Generated!
{/* Subtext */}
@@ -142,22 +190,22 @@ export default function CertificateResult() {
No interns match your search.
) : (
- paginated.map((cert) => (
+ paginated.map((cert: Certificate) => (