Update all hooks (Projects, Tasks, Certificates), implement backend for Projects. HomePage on Mentors

This commit is contained in:
Kemas M. Aryadary Rasyad
2026-06-26 10:59:35 +07:00
parent 2e162bfc1e
commit 53b270921d
34 changed files with 895 additions and 625 deletions
+19
View File
@@ -0,0 +1,19 @@
// hooks/useMahasiswa.ts
import { useState, useEffect } from 'react';
import type { Mahasiswa } from '../types/project.types';
import { getMahasiswa } from '../services/MahasiswaService';
export const useMahasiswa = () => {
const [mahasiswa, setMahasiswa] = useState<Mahasiswa[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
getMahasiswa()
.then(setMahasiswa)
.catch(() => setError('Gagal memuat data mahasiswa.'))
.finally(() => setLoading(false));
}, []);
return { mahasiswa, loading, error };
};
+44
View File
@@ -0,0 +1,44 @@
// hooks/useNotifications.ts
import { useState, useEffect, useCallback } from "react";
import type { Notification } from "../types/certificate.types";
import {
getNotifications,
markAllNotificationsRead,
markNotificationRead,
} from "../services/NotificationService";
export const useNotifications = () => {
const [notifications, setNotifications] = useState<Notification[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const refetch = useCallback(() => {
setLoading(true);
getNotifications()
.then(setNotifications)
.catch(() => setError("Gagal memuat notifikasi."))
.finally(() => setLoading(false));
}, []);
useEffect(() => { refetch(); }, [refetch]);
const markAllRead = async () => {
try {
await markAllNotificationsRead();
setNotifications(prev => prev.map(n => ({ ...n, is_read: true })));
} catch { setError("Gagal menandai semua notifikasi."); }
};
const markOneRead = async (id: number) => {
try {
await markNotificationRead(id);
setNotifications(prev =>
prev.map(n => n.id === id ? { ...n, is_read: true } : n)
);
} catch { setError("Gagal menandai notifikasi."); }
};
const unreadCount = notifications.filter(n => !n.is_read).length;
return { notifications, unreadCount, loading, error, refetch, markAllRead, markOneRead };
};
+138
View File
@@ -0,0 +1,138 @@
// hooks/useCertificates.ts
import { useState, useEffect, useCallback } from "react";
import type { Certificate, CertificateVerifyResult } from "../types/certificate.types";
import {
claimCertificate,
getMyCertificates,
getAllCertificates,
getCertificateDetail,
getCertificatesByProject,
verifyCertificate,
verifyCertificateByUuid,
uploadCertificateTemplate,
} from "../services/CertificateService";
// GET /certificate-api/my-certificates
export const useMyCertificates = () => {
const [certificates, setCertificates] = useState<Certificate[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
getMyCertificates()
.then(setCertificates)
.catch(() => setError("Gagal memuat sertifikat."))
.finally(() => setLoading(false));
}, []);
return { certificates, loading, error };
};
// GET /certificate-api/all
export const useAllCertificates = () => {
const [certificates, setCertificates] = useState<Certificate[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
getAllCertificates()
.then(setCertificates)
.catch(() => setError("Gagal memuat semua sertifikat."))
.finally(() => setLoading(false));
}, []);
return { certificates, loading, error };
};
// GET /certificate-api/detail/{id}
export const useCertificateDetail = (id: number) => {
const [certificate, setCertificate] = useState<Certificate | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
if (!id) return;
getCertificateDetail(id)
.then(setCertificate)
.catch(() => setError("Gagal memuat detail sertifikat."))
.finally(() => setLoading(false));
}, [id]);
return { certificate, loading, error };
};
// GET /certificate-api/project/{id_project}
export const useProjectCertificates = (id_project: number) => {
const [certificates, setCertificates] = useState<Certificate[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
if (!id_project) return;
getCertificatesByProject(id_project)
.then(setCertificates)
.catch(() => setError("Gagal memuat sertifikat project."))
.finally(() => setLoading(false));
}, [id_project]);
return { certificates, loading, error };
};
// GET /certificate-api/verify & verify-uuid (public, on-demand)
export const useVerifyCertificate = () => {
const [result, setResult] = useState<CertificateVerifyResult | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const verifyByNo = useCallback(async (certificate_no: string) => {
setLoading(true); setError(null);
try {
const res = await verifyCertificate(certificate_no);
setResult(res);
return res;
} catch { setError("Sertifikat tidak valid atau tidak ditemukan."); return null; }
finally { setLoading(false); }
}, []);
const verifyByUuid = useCallback(async (uuid: string) => {
setLoading(true); setError(null);
try {
const res = await verifyCertificateByUuid(uuid);
setResult(res);
return res;
} catch { setError("Sertifikat tidak valid atau tidak ditemukan."); return null; }
finally { setLoading(false); }
}, []);
return { result, loading, error, verifyByNo, verifyByUuid };
};
// POST /certificate-api/claim
export const useClaimCertificate = () => {
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const claim = async (id_project: number) => {
setLoading(true); setError(null);
try { return await claimCertificate(id_project); }
catch { setError("Gagal claim sertifikat."); return null; }
finally { setLoading(false); }
};
return { claim, loading, error };
};
// POST /certificate-api/projects/{id_project}/template
export const useUploadCertificateTemplate = () => {
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const upload = async (id_project: number, file: File) => {
setLoading(true); setError(null);
try { return await uploadCertificateTemplate(id_project, file); }
catch { setError("Gagal upload template."); return null; }
finally { setLoading(false); }
};
return { upload, loading, error };
};
+4 -2
View File
@@ -20,7 +20,7 @@ export const useProjectTasks = (projectId: string) => {
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const refetch = useCallback(() => {
if (!projectId) return;
setLoading(true);
getProjectTasks(projectId)
@@ -29,7 +29,9 @@ export const useProjectTasks = (projectId: string) => {
.finally(() => setLoading(false));
}, [projectId]);
return { tasks, loading, error };
useEffect(() => { refetch(); }, [refetch]);
return { tasks, loading, error, refetch }; // tambah refetch
};
// GET /task-api/tasks/{id}