Refactor internify components and services

- Updated LoginInternify to remove unnecessary state initialization.
- Cleaned up NotificationList by removing unused code and fixing button click handler.
- Added project_id prop to InternClaimCert for better data handling.
- Enhanced CertificateNotAvailable to include claim functionality with loading and error states.
- Modified HomeHeader to display a default user name.
- Corrected ProjectTimeline to import ProjectTask from the correct type file.
- Implemented loading and error handling in CertificateListPage.
- Improved MentorProjectDetailPage to handle task updates and navigation correctly.
- Enhanced MentorSettingsPage to manage profile picture errors and state.
- Updated AddInternsDialog to use the correct hook for fetching interns.
- Refactored EditSubmissionTask to handle task updates and errors more effectively.
- Improved MentorProjectCard to include project archiving functionality.
- Removed deprecated useInterns hook and replaced it with useUser for fetching user data.
- Added new useMentors hook for managing mentor-related operations.
- Cleaned up CertificateService by removing unused verifyCertificate function.
- Created MentorServices for managing mentor data operations.
- Updated UserService to include fetching Mahasiswa data and removed unused UserServices file.
This commit is contained in:
Kemas M. Aryadary Rasyad
2026-06-29 15:42:48 +07:00
parent 4084ccf1bf
commit da67ad86d8
29 changed files with 1099 additions and 5581 deletions
@@ -65,6 +65,7 @@ const SertificatePage = () => {
progress={progress}
remainingTasks={remainingTasks}
allTasksDone={allTasksDone}
project_id={project?.id}
/>
)}
</div>
@@ -1,16 +1,27 @@
import { Clock, Lock, Award } from "lucide-react";
import { useClaimCertificate } from "../../../../hooks/useSertificates";
interface CertificateNotAvailableProps {
progress: number;
remainingTasks: number;
allTasksDone?: boolean;
project_id?: number;
}
export default function CertificateNotAvailable({
progress,
remainingTasks,
allTasksDone = false,
project_id
}: CertificateNotAvailableProps) {
const { claim, loading, error } = useClaimCertificate();
const handleClaim = async () => {
if (!project_id) return;
await claim(project_id);
};
return (
<div className="bg-box-primary w-full h-full rounded-2xl border border-box-border shadow-sm px-24 py-12 flex flex-col items-center justify-center">
{/* Icon */}
@@ -59,9 +70,9 @@ export default function CertificateNotAvailable({
{/* Progress Bar */}
<div className="w-1/3">
<p className="text-xs text-font mt-2">
Selesaikan {remainingTasks} tugas lagi untuk membuka akses.
</p>
<p className="text-xs text-font mt-2">
Selesaikan {remainingTasks} tugas lagi untuk membuka akses.
</p>
<div className="flex justify-between items-center mb-2">
<span className="text-sm font-semibold text-gray-700">Progres Proyek</span>
<span className="text-sm font-bold text-red-700">{progress}%</span>
@@ -72,8 +83,17 @@ export default function CertificateNotAvailable({
style={{ width: `${progress}%` }}
/>
</div>
</div>
<button
onClick={handleClaim}
disabled={loading}
className="mt-2 px-6 py-2.5 bg-green-600 hover:bg-green-700 disabled:opacity-50 disabled:cursor-not-allowed text-white font-semibold rounded-lg transition-colors duration-200"
>
{loading ? "Memproses..." : "Klaim Sertifikat"}
</button>
{error && <p className="text-red-500 text-sm mt-2">{error}</p>}
</>
)}
</div>
@@ -17,7 +17,7 @@ const HomeHeader = () => {
return (
<div>
<div className="flex items-start px-0 mb-5">
<h1 className="page-title">Hi, {name || "..."}!</h1>
<h1 className="page-title">Hi, {name || "User"}!</h1>
</div>
</div>
);
@@ -1,4 +1,4 @@
import type { ProjectTask } from "../../../../types/project.types";
import type { ProjectTask } from "../../../../types/task.types";
const formatDate = (dateStr: string) =>
new Date(dateStr).toLocaleDateString('id-ID', { day: 'numeric', month: 'short' });
@@ -6,22 +6,24 @@ const formatDate = (dateStr: string) =>
export default function ProjectTimeline({ tasks }: { tasks: ProjectTask[] }) {
const today = new Date();
today.setHours(0, 0, 0, 0);
return (
<div className="">
<div className="flex items-center gap-1.5 mb-4 justify-between">
<span className="font-semibold text-md text-font-shade">Timeline</span>
</div>
<ul className="relative pl-4">
{tasks.map((task) => {
{tasks.map((task, index) => {
const d = new Date(task.deadline_at);
d.setHours(0, 0, 0, 0);
const isToday = d.getTime() === today.getTime();
const isPast = d < today;
const active = isToday || isPast;
return (
<li key={task.id} className="relative mb-6 list-none">
{/* Vertical line ke bawah, kecuali item terakhir */}
{index < tasks.length - 1 && (
<div className="absolute -left-[1.0625rem] top-3 w-px h-full bg-[#ddd]" />
)}
{/* Dot */}
<div
className={`absolute -left-5 top-0.5 w-3 h-3 rounded-full border-2 border-white ${
@@ -29,7 +31,7 @@ export default function ProjectTimeline({ tasks }: { tasks: ProjectTask[] }) {
} ${isToday ? 'shadow-md' : ''}`}
/>
<div className={`text-[11px] font-semibold ${isToday ? 'text-[#C0392B]' : 'text-[#888]'}`}>
{isToday ? 'Hari ini' : formatDate(task.deadline_at)} {/* ← format ISO string */}
{isToday ? 'Hari ini' : formatDate(task.deadline_at)}
</div>
<div className="text-xs text-[#555] mt-px leading-[1.3]">
{task.title}