From 1ee784e70d80a7cce68462d984897561b82f1e0c Mon Sep 17 00:00:00 2001 From: "Kemas M. Aryadary Rasyad" <112590882+aryadary0706@users.noreply.github.com> Date: Mon, 27 Jul 2026 14:37:54 +0700 Subject: [PATCH] feat: enhance project intern management with improved loading states and refetch functionality --- src/Pages/internify/mentor/ProjectTaskTab.tsx | 54 ++++--------------- .../internify/mentor/ProjectsInternTab.tsx | 26 +++++---- src/Pages/internify/mentor/ProjectsPage.tsx | 4 +- .../mentor/components/AddInternsDialog.tsx | 47 ++-------------- .../mentor/components/EligibleInternTable.tsx | 2 +- .../mentor/components/SidebarMentor.tsx | 5 +- src/hooks/useProjects.ts | 6 ++- 7 files changed, 40 insertions(+), 104 deletions(-) diff --git a/src/Pages/internify/mentor/ProjectTaskTab.tsx b/src/Pages/internify/mentor/ProjectTaskTab.tsx index d594186..6d0739d 100644 --- a/src/Pages/internify/mentor/ProjectTaskTab.tsx +++ b/src/Pages/internify/mentor/ProjectTaskTab.tsx @@ -26,9 +26,7 @@ export default function TaskTab({ project }: { project: ProjectDetail }) { if (loading) return (
- {[...Array(3)].map((_, i) => ( -
- ))} +
); @@ -41,7 +39,7 @@ export default function TaskTab({ project }: { project: ProjectDetail }) { @@ -68,18 +66,18 @@ export default function TaskTab({ project }: { project: ProjectDetail }) { {task.title}
-
+
@@ -93,43 +91,6 @@ export default function TaskTab({ project }: { project: ProjectDetail }) {
) } - - {/* {tasks.length > 0 ? ( - tasks.map((task, idx) => ( -
-
- - {task.title} -
-
- - -
-
- )) - ) : ( -
- -

Belum ada tugas yang dibuat untuk proyek ini.

-
- )} */}
{isModalOpen && ( @@ -143,7 +104,10 @@ export default function TaskTab({ project }: { project: ProjectDetail }) { {editModalOpen && selectedTask && ( { setEditModalOpen(false); setSelectedTask(null); }} + onClose={() => { + setEditModalOpen(false); + setSelectedTask(null); + }} task={selectedTask} onSuccess={() => { setEditModalOpen(false); setSelectedTask(null); refetch(); }} /> diff --git a/src/Pages/internify/mentor/ProjectsInternTab.tsx b/src/Pages/internify/mentor/ProjectsInternTab.tsx index 0a489f8..17b5de4 100644 --- a/src/Pages/internify/mentor/ProjectsInternTab.tsx +++ b/src/Pages/internify/mentor/ProjectsInternTab.tsx @@ -2,30 +2,30 @@ import { useState } from 'react'; import { Plus, Users } from 'lucide-react'; import ManageInternsModal from './components/AddInternsDialog'; import type { ProjectDetail, ProjectMember } from '../../../types/project.types'; -import { useRemoveMember } from '../../../hooks/useProjects'; +import { useProjectInterns, useRemoveMember } from '../../../hooks/useProjects'; import { customToast } from '../../utils/showToast'; import { getInitials } from '../../../hooks/useUser'; import { resolveFileUrl } from '../../utils/resolveFileFromUrl'; export default function InternsTab({ project }: { project: ProjectDetail }) { const [isModalOpen, setIsModalOpen] = useState(false); - const [members, setMembers] = useState(project.members); - const { remove, loading } = useRemoveMember(); - const isProjectActive = project?.status === "active" + const { interns: members, loading, error, refetch } = useProjectInterns(String(project.id)); + const { remove, loading: removing } = useRemoveMember(); + const isProjectActive = project?.status === "active"; const [adminImageError, setAdminImageError] = useState(false); const adminAvatar = resolveFileUrl(project.admin.profile_picture); const adminHasPhoto = !!adminAvatar && !adminImageError; - const handleModalClose = (finalMembers: ProjectMember[]) => { - setMembers(finalMembers); + const handleModalClose = () => { setIsModalOpen(false); + refetch(); }; const handleRemove = async (id_user: number) => { const res = await remove({ id_project: project.id, id_user }); if (res.success) { - setMembers(prev => prev.filter(m => m.id !== id_user)); customToast.success('Member removed', res.message); + refetch(); } else { customToast.error('Failed to remove', res.message); } @@ -78,7 +78,15 @@ export default function InternsTab({ project }: { project: ProjectDetail }) {
- {members.length === 0 ? ( + {loading ? ( +
+ {[...Array(3)].map((_, i) => ( +
+ ))} +
+ ) : error ? ( +

{error}

+ ) : members.length === 0 ? (

Belum ada anggota.

@@ -101,7 +109,7 @@ export default function InternsTab({ project }: { project: ProjectDetail }) {
diff --git a/src/Pages/internify/mentor/components/EligibleInternTable.tsx b/src/Pages/internify/mentor/components/EligibleInternTable.tsx index 9983c36..5cbe202 100644 --- a/src/Pages/internify/mentor/components/EligibleInternTable.tsx +++ b/src/Pages/internify/mentor/components/EligibleInternTable.tsx @@ -118,7 +118,7 @@ export default function EligibleInternTable({ id: intern.id_user, id_project: project.id, id_user: intern.id_user, - certificate_no: "CERT/yymmxx/XXX", + certificate_no: "CERT/yyddmm/X", issued_at: new Date().toISOString().slice(0, 10), uuid: "HANYA SEBATAS PREVIEW", user: { diff --git a/src/Pages/internify/mentor/components/SidebarMentor.tsx b/src/Pages/internify/mentor/components/SidebarMentor.tsx index 03141ff..e246b4d 100644 --- a/src/Pages/internify/mentor/components/SidebarMentor.tsx +++ b/src/Pages/internify/mentor/components/SidebarMentor.tsx @@ -1,13 +1,12 @@ import { useNavigate, useLocation } from 'react-router-dom'; -import { Home, FolderKanban, UsersRound, Medal, Settings, HelpCircle, LogOut, X } from 'lucide-react'; +import { Home, FolderKanban, UsersRound, FileBadge , Settings, HelpCircle, LogOut, X } from 'lucide-react'; import { FaUserTie } from "react-icons/fa"; const navItems = [ - // belum kutambahin { label: "Home", icon: Home, path: "/mentor" }, { label: "List Projects", icon: FolderKanban, path: "/mentor/projects" }, { label: "Interns", icon: UsersRound, path: "/mentor/intern" }, - { label: "Certificates", icon: Medal, path: "/mentor/certificates" }, + { label: "Certificates", icon: FileBadge, path: "/mentor/certificates" }, ]; const prefItems = [ diff --git a/src/hooks/useProjects.ts b/src/hooks/useProjects.ts index dad9988..6d4b182 100644 --- a/src/hooks/useProjects.ts +++ b/src/hooks/useProjects.ts @@ -73,7 +73,7 @@ export const useProjectInterns = (projectId: string) => { const [loading, setLoading] = useState(false); const [error, setError] = useState(null); - useEffect(() => { + const refetch = useCallback(() => { if (!projectId) return; setLoading(true); getProjectMembers(projectId) @@ -82,7 +82,9 @@ export const useProjectInterns = (projectId: string) => { .finally(() => setLoading(false)); }, [projectId]); - return { interns, loading, error }; + useEffect(() => { refetch(); }, [refetch]); + + return { interns, loading, error, refetch }; }; // GET /project-api/my-projects