diff --git a/src/Pages/internify/intern/ProjectDetailPage.tsx b/src/Pages/internify/intern/ProjectDetailPage.tsx
index d27c2c0..fa8a315 100644
--- a/src/Pages/internify/intern/ProjectDetailPage.tsx
+++ b/src/Pages/internify/intern/ProjectDetailPage.tsx
@@ -8,8 +8,10 @@ export default function ProjectDetailPage() {
const { slug } = useParams<{ slug: string }>();
const { project, loading, error } = useProjectDetail(slug ?? "");
const [activeTab, setActiveTab] = useState<'forum' | 'participants'>('forum');
+
if (loading) return
Memuat project...
;
if (error) return
{error}
;
+
if (!project) return
Project tidak ditemukan.
;
const tabClass = (tab: string) =>
diff --git a/src/Pages/internify/intern/SertificateList.tsx b/src/Pages/internify/intern/SertificateList.tsx
index 74a7264..475d89f 100644
--- a/src/Pages/internify/intern/SertificateList.tsx
+++ b/src/Pages/internify/intern/SertificateList.tsx
@@ -1,8 +1,11 @@
-import { Award } from "lucide-react";
-import { history, type CertificateHistory } from "../../../lib/mockSertificates";
+import { Award, Loader2, AlertCircle } from "lucide-react";
+import { useMyCertificates } from "../../../hooks/useSertificates";
+import type { Certificate } from "../../../types/certificate.types";
import certificateImg from "../../../assets/certificate.png";
export default function SertificateList() {
+ const { certificates, loading, error } = useMyCertificates();
+
return (
{/* Header */}
@@ -11,15 +14,33 @@ export default function SertificateList() {
All your earned internship certificates
- {/* Grid */}
- {history.length === 0 ? (
+ {/* Loading */}
+ {loading && (
+
+
+ Loading certificates...
+
+ )}
+
+ {/* Error */}
+ {error && !loading && (
+
+ )}
+
+ {/* Empty */}
+ {!loading && !error && certificates.length === 0 && (
- ) : (
+ )}
+
+ {/* Grid */}
+ {!loading && !error && certificates.length > 0 && (
- {history.map((item: CertificateHistory) => (
+ {certificates.map((item: Certificate) => (
@@ -37,9 +58,18 @@ export default function SertificateList() {
{/* Info */}
-
{item.month}
-
{item.title}
-
Grade: {item.grade}
+
+ {new Date(item.issued_at).toLocaleDateString("id-ID", {
+ year: "numeric",
+ month: "long",
+ })}
+
+
+ {item.project.project_name}
+
+
+ No: {item.certificate_no}
+
))}
diff --git a/src/Pages/internify/intern/SertificatePage.tsx b/src/Pages/internify/intern/SertificatePage.tsx
index 004bb47..4f6bc72 100644
--- a/src/Pages/internify/intern/SertificatePage.tsx
+++ b/src/Pages/internify/intern/SertificatePage.tsx
@@ -1,31 +1,81 @@
-import { certificateData } from "../../../lib/mockSertificates";
+import { useState, useMemo } from "react";
import CertificateAvailable from "./components/CertificateAvailabe";
import CertificateNotAvailable from "./components/CertificateNotAvailable";
import SertificateHistory from "./components/CertificateHistory";
+import { useMyCertificates, useClaimCertificate } from "../../../hooks/useSertificates";
+import { useMyProjects } from "../../../hooks/useProjects";
+import type { ProjectDetail } from "../../../types/project.types";
const SertificatePage = () => {
- const data = certificateData;
+ const { projects, loading: projectsLoading } = useMyProjects();
+ const { certificates, loading: certLoading } = useMyCertificates();
+ const { claim, loading: claiming } = useClaimCertificate();
+ const [justClaimed, setJustClaimed] = useState(false);
+ const current_task = useMemo(() => {
+ if (!projects || projects.length === 0) return null;
+ return projects.find((p) => p.task_done < p.total_tasks) ?? null;
+ }, [projects]);
+
+ const finished_task = useMemo((): ProjectDetail | null => {
+ if (!projects || projects.length === 0) return null;
+ return projects.find((p) => p.task_done >= p.total_tasks) ?? null;
+ }, [projects]);
+
+ // Cek apakah finished_task sudah punya certificate di backend
+ const alreadyClaimed = useMemo(() => {
+ if (!finished_task || !certificates.length) return false;
+ return certificates.some((cert) => cert.project.id === finished_task.id);
+ }, [certificates, finished_task]);
+
+ const allTasksDone = !projectsLoading && current_task === null && projects.length > 0;
+
+ const progress =
+ current_task && current_task.total_tasks > 0
+ ? Math.round((current_task.task_done / current_task.total_tasks) * 100)
+ : 100;
+
+ const remainingTasks = current_task
+ ? current_task.total_tasks - current_task.task_done
+ : 0;
+
+ const handleClaim = async () => {
+ if (!finished_task) return;
+ const result = await claim(finished_task.id);
+ if (result) setJustClaimed(true);
+ };
+
+ const isLoading = projectsLoading || certLoading;
+ const showCertificate = alreadyClaimed || justClaimed;
+
+ if (isLoading) {
+ return (
+
+ Loading...
+
+ );
+ }
+ console.log(finished_task)
return (
- {/* Header */}
Certificates
View and download internship certificates
- {/* Kiri: Content */}
- {data.isReceived ? (
-
+ {showCertificate ? (
+
) : (
)}
- {/* Kanan: History */}
@@ -34,4 +84,4 @@ const SertificatePage = () => {
);
};
-export default SertificatePage;
+export default SertificatePage;
\ No newline at end of file
diff --git a/src/Pages/internify/intern/TaskSubmission.tsx b/src/Pages/internify/intern/TaskSubmission.tsx
index f149654..29e5ea2 100644
--- a/src/Pages/internify/intern/TaskSubmission.tsx
+++ b/src/Pages/internify/intern/TaskSubmission.tsx
@@ -49,13 +49,15 @@ export default function TaskSubmission() {
{submissionType === "file_upload" ? (
) : (
diff --git a/src/Pages/internify/intern/components/CertificateAvailabe.tsx b/src/Pages/internify/intern/components/CertificateAvailabe.tsx
index 028f62d..70757f6 100644
--- a/src/Pages/internify/intern/components/CertificateAvailabe.tsx
+++ b/src/Pages/internify/intern/components/CertificateAvailabe.tsx
@@ -1,10 +1,13 @@
import { useState } from "react";
import { Download, Link, Printer } from "lucide-react";
-import { type CertificateData } from "../../../../lib/mockSertificates";
+import type { ProjectDetail } from "../../../../types/project.types";
import certificateImg from "../../../../assets/certificate.png";
-export default function CertificateAvailable({ data }: { data: CertificateData }) {
- const { certificate } = data;
+interface CertificateAvailableProps {
+ project: ProjectDetail | null;
+}
+
+export default function CertificateAvailable({ project }: CertificateAvailableProps) {
const [copied, setCopied] = useState(false);
const handleCopyLink = () => {
@@ -13,10 +16,25 @@ export default function CertificateAvailable({ data }: { data: CertificateData }
setTimeout(() => setCopied(false), 2000);
};
+ // Hitung durasi dari start_date - end_date
+ const duration = project
+ ? (() => {
+ const start = new Date(project.start_date);
+ const end = new Date(project.end_date);
+ const diffMs = end.getTime() - start.getTime();
+ const diffDays = Math.ceil(diffMs / (1000 * 60 * 60 * 24));
+ const months = Math.floor(diffDays / 30);
+ const days = diffDays % 30;
+ if (months > 0 && days > 0) return `${months} Bulan ${days} Hari`;
+ if (months > 0) return `${months} Bulan`;
+ return `${diffDays} Hari`;
+ })()
+ : "-";
+
return (
{/* Certificate Card */}
-
+
{[
- { label: "Nama Program", value: certificate.programName },
- { label: "Durasi", value: certificate.duration },
+ { label: "Nama Program", value: project?.project_name ?? "-" },
+ { label: "Durasi", value: duration },
+ { label: "Total Tugas", value: project ? `${project.total_tasks} Tugas` : "-" },
{
label: "Status",
value: (
- VERIFIED
+ UNVERIFIED
),
},
diff --git a/src/Pages/internify/intern/components/CertificateHistory.tsx b/src/Pages/internify/intern/components/CertificateHistory.tsx
index 0f0048d..55b37e5 100644
--- a/src/Pages/internify/intern/components/CertificateHistory.tsx
+++ b/src/Pages/internify/intern/components/CertificateHistory.tsx
@@ -1,38 +1,69 @@
import { useNavigate } from "react-router-dom";
-import { History } from "lucide-react";
-import { type CertificateHistory, history as certificateHistory } from "../../../../lib/mockSertificates";
+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";
export default function SertificateHistory() {
const navigate = useNavigate();
+ const { certificates, loading, error } = useMyCertificates();
+ const displayed = certificates.slice(0, 2);
+
return (
-
+
Certificate History
-
- {certificateHistory.slice(0, 2).map((item: CertificateHistory) => (
-
-
-

+ {loading && (
+
+
+ Loading certificates...
+
+ )}
+
+ {error && !loading && (
+
+ )}
+
+ {!loading && !error && displayed.length === 0 && (
+
No certificates yet.
+ )}
+
+ {!loading && !error && displayed.length > 0 && (
+
+ {displayed.map((item: Certificate) => (
+
+
+

+
+
+
+ {new Date(item.issued_at).toLocaleDateString("id-ID", {
+ year: "numeric",
+ month: "long",
+ })}
+
+
+ {item.project.project_name}
+
+
No: {item.certificate_no}
+
-
-
{item.month}
-
{item.title}
-
Grade: {item.grade}
-
-
- ))}
-
+ ))}
+
+ )}