diff --git a/src/Layout/Header-internify.tsx b/src/Layout/Header-internify.tsx index 7e4b8bd..3b6a78b 100644 --- a/src/Layout/Header-internify.tsx +++ b/src/Layout/Header-internify.tsx @@ -2,7 +2,7 @@ import { Bell, Menu } from "lucide-react" import { useEffect, useState } from "react"; import humiclogo from "../assets/logo.png"; import { useNavigate } from "react-router-dom"; -import { useCurrentUser } from "../hooks/useUser"; +import { useCurrentUser, resolveFileUrl, DEFAULT_AVATAR } from "../hooks/useUser"; function getInitials(fullName: string | undefined | null): string { if (!fullName) return "U"; @@ -17,15 +17,12 @@ export default function Header({ toggleSidebar }: { toggleSidebar: () => void }) const { user } = useCurrentUser(); const [imageError, setImageError] = useState(false); - useEffect(() => { - setImageError(false); - }, [user?.profile_picture, user?.full_name]); + const displayName = user?.full_name?.trim() || [user?.nama_depan, user?.nama_belakang].filter(Boolean).join(" ") || ""; - const settingsPath = - user?.role === "intern" ? "/intern" : "/mentor"; + const photoUrl = resolveFileUrl(user?.profile_picture); - const initials = user?.full_name ? getInitials(user.full_name) : "U"; - const hasPhoto = !!user?.profile_picture && !imageError; + const initials = getInitials(displayName); + const hasPhoto = !!photoUrl && !imageError; return (
@@ -49,13 +46,12 @@ export default function Header({ toggleSidebar }: { toggleSidebar: () => void }) {/* Gambar Profil */} - {/* Forgot Password */}
@@ -134,17 +134,15 @@ export default function InternifyLogin() { {/* Divider & Support */}
-
+
SUPPORT -
+
-
diff --git a/src/Pages/internify/NotificationList.tsx b/src/Pages/internify/NotificationList.tsx index e3afa68..702a20d 100644 --- a/src/Pages/internify/NotificationList.tsx +++ b/src/Pages/internify/NotificationList.tsx @@ -5,6 +5,7 @@ import { LuFileSpreadsheet } from "react-icons/lu"; import { io } from "socket.io-client"; import * as NotificationService from "../../services/NotificationService"; import type { UINotification, BackendNotification } from "../../types/notification.types"; +import mailRafiki from '../../assets/Mail-rafiki.svg' const getCookieToken = () => { return document.cookie @@ -95,7 +96,10 @@ export default function NotificationList() {
{notifications.length === 0 && ( -

Belum ada notifikasi.

+
+ empty notification +

Belum ada pesan notifikasi yang diterima

+
)}
@@ -165,9 +169,9 @@ export default function NotificationList() { {/* Header Section YESTERDAY */}
-

+ {/*

YESTERDAY -

+ */} {notifications.filter(n => n.group === "YESTERDAY").map((item) => (
(null); const [photoPreview, setPhotoPreview] = useState(null); + const [removePhoto, setRemovePhoto] = useState(false); const [imageError, setImageError] = useState(false); const fileInputRef = useRef(null); useEffect(() => { - if (user) { - setFormData({ - fullName: - user.full_name || "", - email: user.email || "", - bio: user.professional_bio || "", - }); - if (user.profile_picture) { - setPhotoPreview(user.profile_picture); - } else { - setPhotoPreview(null); - } - setImageError(false); - } + if (!user) return; + + setFormData({ + fullName: user.full_name || `${user.nama_depan} ${user.nama_belakang}`.trim(), + email: user.email || "", + bio: user.professional_bio || "", + }); + + setPhotoPreview(resolveFileUrl(user.profile_picture) ?? DEFAULT_AVATAR); + setRemovePhoto(false); + setImageError(false); }, [user]); const handleChange = (e: React.ChangeEvent) => { @@ -43,46 +43,50 @@ export default function SettingsPage() { const handlePhotoUpload = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; - if (file) { - setPhotoFile(file); - const reader = new FileReader(); - reader.onloadend = () => setPhotoPreview(reader.result as string); - reader.readAsDataURL(file); - } + if (!file) return; + + setPhotoFile(file); + setRemovePhoto(false); + setImageError(false); + + const reader = new FileReader(); + reader.onloadend = () => setPhotoPreview(reader.result as string); + reader.readAsDataURL(file); }; const handleRemovePhoto = () => { setPhotoFile(null); - setPhotoPreview(null); + setRemovePhoto(true); + setPhotoPreview(DEFAULT_AVATAR); setImageError(false); if (fileInputRef.current) fileInputRef.current.value = ""; }; - const handleSaveChanges = async (e: React.FormEvent) => { - e.preventDefault(); - - await customToast.promise( - save({ - full_name: formData.fullName, - email: formData.email, - professional_bio: formData.bio, - }), - { - loading: "Menyimpan perubahan...", - success: () => ({ - title: "Perubahan berhasil disimpan!", - description: successMsg || "", + const handleSaveChanges = async () => { + setConfirmOpen(false); + try { + await customToast.promise( + save({ + full_name: formData.fullName, + email: formData.email, + professional_bio: formData.bio, + profile_picture: photoFile ?? (removePhoto ? DEFAULT_AVATAR : undefined), }), - error: () => ({ - title: "Gagal menyimpan perubahan!", - description: errorMsg || "", - }), - } - ); + { + loading: "Menyimpan perubahan...", + success: () => ({ + title: "Perubahan berhasil disimpan!", + description: msgRef.current.success ?? "", + }), + error: () => ({ + title: "Gagal menyimpan perubahan!", + description: msgRef.current.error ?? "", + }), + } + ); + } catch { } }; - const initialLetter = formData.fullName ? formData.fullName.charAt(0).toUpperCase() : "U"; - if (userLoading) { return (
@@ -91,6 +95,11 @@ export default function SettingsPage() { ); } + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + setConfirmOpen(true); + }; + return (
{/* Header Title */} @@ -113,22 +122,20 @@ export default function SettingsPage() {
{/* Card Body & Form */} -
+ {/* Profile Photo Row */}
{/* Avatar dengan badge edit */}
-
- {photoPreview && !imageError ? ( +
+ {photoPreview && !imageError && ( Profile setImageError(true)} /> - ) : ( - {initialLetter} )}
{/* Badge edit icon */} @@ -237,18 +244,6 @@ export default function SettingsPage() {

- {/* Feedback Messages - {successMsg && ( -

- {successMsg} -

- )} - {errorMsg && ( -

- {errorMsg} -

- )} */} - {/* Footer Action */}
+ setConfirmOpen(false)} + />
); } \ No newline at end of file diff --git a/src/Pages/internify/UpdateProfileDialogue.tsx b/src/Pages/internify/UpdateProfileDialogue.tsx new file mode 100644 index 0000000..8a6e9a5 --- /dev/null +++ b/src/Pages/internify/UpdateProfileDialogue.tsx @@ -0,0 +1,116 @@ +import { useEffect, useRef } from "react"; +import { AlertTriangle, X } from "lucide-react"; + +interface UpdateProfileDialogProps { + isOpen: boolean; + isIntern: boolean; + loading?: boolean; + onConfirm: () => void; + onClose: () => void; +} + +export default function UpdateProfileDialog({ + isOpen, + isIntern, + loading = false, + onConfirm, + onClose, +}: UpdateProfileDialogProps) { + const dialogRef = useRef(null); + + useEffect(() => { + const dialog = dialogRef.current; + if (!dialog) return; + + if (isOpen && !dialog.open) dialog.showModal(); + if (!isOpen && dialog.open) dialog.close(); + }, [isOpen]); + + // Event "cancel" dipicu saat user menekan Esc. + // preventDefault() tanpa onClose() = Esc benar-benar dimatikan, + // dialog hanya bisa ditutup lewat tombol Batal / X. + useEffect(() => { + const dialog = dialogRef.current; + if (!dialog) return; + + const handleCancel = (e: Event) => e.preventDefault(); + + dialog.addEventListener("cancel", handleCancel); + return () => dialog.removeEventListener("cancel", handleCancel); + }, []); + + return ( + dialog center horizontal & vertical. + // hidden + open:flex -> hanya tampil kalau attribute [open] ada, + // supaya lifecycle showModal()/close() tetap dihormati. + className="hidden open:flex fixed inset-0 m-auto h-fit max-h-[90vh] w-[90vw] max-w-md flex-col overflow-hidden rounded-xl border border-box-border p-0 shadow-xl backdrop:bg-black/40 backdrop:backdrop-blur-[2px]" + onClick={(e) => { + // klik di area backdrop (target === dialog itu sendiri) -> tutup + if (e.target === dialogRef.current && !loading) onClose(); + }} + > + {/* Header */} +
+
+
+

Simpan Perubahan

+
+
+ + +
+ + {/* Body */} +
+

+ Perubahan pada profil kamu akan langsung diterapkan setelah dikonfirmasi. +

+ + {/* Peringatan khusus intern: identitas hanya bisa diubah 1x */} + {isIntern && ( +
+ +
+

Perubahan bersifat permanen

+

+ Sebagai intern, kamu hanya dapat mengubah data identitas{" "} + satu kali. Data ini akan dipakai untuk + pencetakan sertifikat, jadi pastikan penulisannya sudah sesuai. +

+
+
+ )} +
+ + {/* Footer */} +
+ + +
+
+ ); +} \ No newline at end of file diff --git a/src/Pages/internify/intern/ForumTab.tsx b/src/Pages/internify/intern/ForumTab.tsx index 8906031..869e9ff 100644 --- a/src/Pages/internify/intern/ForumTab.tsx +++ b/src/Pages/internify/intern/ForumTab.tsx @@ -3,6 +3,7 @@ import TaskCard from './components/ProjectTaskCard'; import HeroProject from './components/Hero-project'; import { useProjectMyTasks } from '../../../hooks/useProjects'; import type { Project } from '../../../types/project.types'; +import { FileText } from 'lucide-react'; export default function ForumTab({ project }: { project: Project }) { const { tasks, loading, error } = useProjectMyTasks(project.id); @@ -11,29 +12,26 @@ export default function ForumTab({ project }: { project: Project }) {
- {/* Loading skeleton */} {loading && (
{[...Array(3)].map((_, i) => ( -
+
))}
)} - - {/* Error */} {error &&

{error}

} {tasks.length === 0 ? ( -

Belum ada tugas yang dapat dikerjakan.

+
+ +

Belum ada tugas yang dapat dikerjakan.

+
) : ( !loading && (
- {/* Kolom kiri: Timeline */}
- - {/* Kolom kanan: Task cards */}
{tasks.map((task) => ( diff --git a/src/Pages/internify/intern/InternCertDetail.tsx b/src/Pages/internify/intern/InternCertDetail.tsx index 2d0cedf..15c3cd8 100644 --- a/src/Pages/internify/intern/InternCertDetail.tsx +++ b/src/Pages/internify/intern/InternCertDetail.tsx @@ -5,7 +5,6 @@ import CertificateAvailable from "./components/CertificateAvailabe"; import CertificateNotAvailable from "./components/CertificateNotAvailable"; import SertificateHistory from "./components/CertificateHistory"; -// import { useClaimCertificate } from "../../../hooks/useCertificates"; import { useProjectDetail } from "../../../hooks/useProjects"; import { useMyCertificates } from "../../../hooks/useCertificates"; import type { Project } from "../../../types/project.types"; @@ -17,7 +16,7 @@ export default function InternCertificateDetail () { const { project: projectDetail, loading: projectLoading } = useProjectDetail(slug ?? ""); const { certificates, loading: certLoading, refetch: refetchCertificates } = useMyCertificates(); - // const { claim, loading: claiming, error: claimError } = useClaimCertificate(); + const myCertificateForProject = useMemo(() => { if (!projectDetail) return null; @@ -42,14 +41,6 @@ export default function InternCertificateDetail () { const remainingTasks = project ? project.total_tasks - project.task_done : 0; const isLoading = projectLoading || certLoading; - // const handleClaim = async () => { - // if (!project?.id) return; - // const result = await claim(project.id); - // if (result) { - // refetchCertificates(); - // } - // }; - if (isLoading) { return (
@@ -77,9 +68,6 @@ export default function InternCertificateDetail () { progress={progress} remainingTasks={remainingTasks} allTasksDone={allTasksDone} - // onClaim={handleClaim} - // claiming={claiming} - // claimError={claimError} /> )}
diff --git a/src/Pages/internify/intern/InternCertList.tsx b/src/Pages/internify/intern/InternCertList.tsx index 9272dc9..a0d4e50 100644 --- a/src/Pages/internify/intern/InternCertList.tsx +++ b/src/Pages/internify/intern/InternCertList.tsx @@ -1,10 +1,30 @@ -import { Loader2, AlertCircle } from "lucide-react"; +import { AlertCircle } from "lucide-react"; import { useMyProjects } from "../../../hooks/useProjects"; import type { Project } from "../../../types/project.types"; import { useNavigate } from "react-router-dom"; import * as LucideIcons from 'lucide-react'; import type { LucideProps } from 'lucide-react'; +import emptyimage from '../../../assets/Learning-amico.svg' +// Skeleton card that mirrors the real certificate card layout +function CertCardSkeleton() { + return ( +
+ {/* Thumbnail */} +
+
+
+
+
+ {/* Info */} +
+
+
+
+
+
+ ); +} export default function SertificateList() { const { projects, loading, error } = useMyProjects(); @@ -23,11 +43,12 @@ export default function SertificateList() {

All your earned internship certificates

- {/* Loading */} + {/* Loading Skeleton */} {loading && ( -
- - Loading certificates... +
+ {[...Array(6)].map((_, i) => ( + + ))}
)} @@ -41,8 +62,9 @@ export default function SertificateList() { {/* Empty */} {!loading && !error && projects.length === 0 && ( -
-

No ongoing projects yet.

+
+ empty image +

Tidak ada projek yang berlangsung.

)} diff --git a/src/Pages/internify/intern/ParticipantTab.tsx b/src/Pages/internify/intern/ParticipantTab.tsx index 82bf71f..c71e596 100644 --- a/src/Pages/internify/intern/ParticipantTab.tsx +++ b/src/Pages/internify/intern/ParticipantTab.tsx @@ -1,3 +1,4 @@ +import { Users } from 'lucide-react'; import type { ProjectDetail, ProjectMember } from '../../../types/project.types'; export default function ParticipantsTab({ project }: { project: ProjectDetail }) { @@ -25,30 +26,35 @@ export default function ParticipantsTab({ project }: { project: ProjectDetail }) {/* Members */}

Members

- {project.members.map((member: ProjectMember, idx) => ( -
-
- {member.avatar ? ( - {member.full_name} - ) : ( -
- )} -
- {member.full_name} - {member.email} -
-
- {member.kelompok_peminatan} + {project.members.length === 0 ? ( +
+ +

Belum ada anggota.

- ))} - - {project.members.length === 0 && ( -

Belum ada members.

+ ) : ( + <> + {project.members.map((member: ProjectMember, idx) => ( +
+
+ {member.avatar ? ( + {member.full_name} + ) : ( +
+ )} +
+ {member.full_name} + {member.email} +
+
+ {member.kelompok_peminatan} +
+ ))} + )}
diff --git a/src/Pages/internify/intern/ProjectsPage.tsx b/src/Pages/internify/intern/ProjectsPage.tsx index 7733cf0..afe36fe 100644 --- a/src/Pages/internify/intern/ProjectsPage.tsx +++ b/src/Pages/internify/intern/ProjectsPage.tsx @@ -1,5 +1,6 @@ import ProjectCard from './components/ProjectsCard' import { useMyProjects } from '../../../hooks/useProjects'; +import TeamPana from '../../../assets/Team-pana.svg' export default function ProjectsPage() { const { projects, loading, error } = useMyProjects(); @@ -21,12 +22,19 @@ export default function ProjectsPage() {

View assigned projects and track their progress.

- {/* Grid card */} -
- {projects.map(project => ( - - ))} -
+ {projects.length === 0 ? ( +
+ +

Kamu belum tergabung ke projek apapun

+
+ ) : ( + // Grid card +
+ {projects.map(project => ( + + ))} +
+ )}
) } \ No newline at end of file diff --git a/src/Pages/internify/intern/components/CertificateNotAvailable.tsx b/src/Pages/internify/intern/components/CertificateNotAvailable.tsx index 173e708..2ca3845 100644 --- a/src/Pages/internify/intern/components/CertificateNotAvailable.tsx +++ b/src/Pages/internify/intern/components/CertificateNotAvailable.tsx @@ -5,18 +5,12 @@ interface CertificateNotAvailableProps { progress: number; remainingTasks: number; allTasksDone?: boolean; - // onClaim: () => void; - // claiming: boolean; - // claimError: string | null; } export default function CertificateNotAvailable({ progress, remainingTasks, allTasksDone = false, - // onClaim, - // claiming, - // claimError, }: CertificateNotAvailableProps) { return (
@@ -54,14 +48,6 @@ export default function CertificateNotAvailable({
- {/* - {claimError &&

{claimError}

} */} ) : ( <> diff --git a/src/Pages/internify/intern/components/HomeProjectList.tsx b/src/Pages/internify/intern/components/HomeProjectList.tsx index 1fe9408..9b05ca6 100644 --- a/src/Pages/internify/intern/components/HomeProjectList.tsx +++ b/src/Pages/internify/intern/components/HomeProjectList.tsx @@ -1,16 +1,18 @@ import { useNavigate } from 'react-router-dom'; import { useProjects } from '../../../../hooks/useProjects'; import { getProjectIcon } from '../../../../lib/ProjectIcons'; +import { RiFileList2Fill } from "react-icons/ri"; const toSlug = (name: string) => name.toLowerCase().replace(/\s+/g, '-'); const HomeProjectList = () => { const navigate = useNavigate(); const { projects, loading, error } = useProjects(); + if (loading) return (
{[...Array(3)].map((_, i) => ( -
+
))}
); @@ -19,27 +21,38 @@ const HomeProjectList = () => { return (
-

Projects

-
- {projects.map((project) => ( -
navigate(`/intern/projects/${toSlug(project.slug)}`)} - className="flex items-center gap-4 border border-card-outline rounded-lg px-4 py-3 hover:bg-gray-50 cursor-pointer transition-colors duration-150" - > -
- {(() => { - const Icon = getProjectIcon(project.project_icon); - return ; - })()} +

Projects

+ + {projects.length === 0 ? ( +
+ +

Kamu belum tergabung ke projek apapun

+
+ ) : ( +
+ {projects.map((project) => ( +
navigate(`/intern/projects/${toSlug(project.slug)}`)} + className="flex items-center gap-4 border border-card-outline rounded-lg px-4 py-3 hover:bg-gray-50 cursor-pointer transition-colors duration-150" + > +
+ {(() => { + const Icon = getProjectIcon(project.project_icon); + return ; + })()} +
+
+

{project.project_name}

+

{project.status}

+
-
-

{project.project_name}

-

{project.status}

-
-
- ))} -
+ ))} +
+ )}
); }; diff --git a/src/Pages/internify/intern/components/SidebarIntern.tsx b/src/Pages/internify/intern/components/SidebarIntern.tsx index 278aeba..3c245f5 100644 --- a/src/Pages/internify/intern/components/SidebarIntern.tsx +++ b/src/Pages/internify/intern/components/SidebarIntern.tsx @@ -2,6 +2,7 @@ import { useNavigate, useLocation } from 'react-router-dom'; import { Award, Settings, HelpCircle, X } from 'lucide-react'; import { GrHomeRounded } from "react-icons/gr"; import { MdOutlineAssignment, MdLogout } from "react-icons/md"; +import { HiUser } from "react-icons/hi2"; const navItems = [ { label: "Home", icon: GrHomeRounded, path: "/intern" }, @@ -46,12 +47,14 @@ export default function SidebarIntern({ isOpen, closeSidebar }: SidebarProps) { `} > {/* Brand & Tombol Close untuk Mobile */} -
+
-
+
+ +
-

Internify

-

LMS Portal

+

Internify

+

LMS Portal

- {members.map((member: ProjectMember, idx) => ( + {members.length === 0 ? ( +
+ +

Belum ada anggota.

+
+ ) : ( + members.map((member: ProjectMember, idx) => (
- ))} + )))}
{isModalOpen && )) ) : ( -
- Belum ada tugas yang dibuat untuk proyek ini. +
+ +

Belum ada tugas yang dibuat untuk proyek ini.

)}
diff --git a/src/Pages/internify/mentor/components/AddInternsToProjects.tsx b/src/Pages/internify/mentor/components/AddInternsToProjects.tsx index 14bda72..28d7458 100644 --- a/src/Pages/internify/mentor/components/AddInternsToProjects.tsx +++ b/src/Pages/internify/mentor/components/AddInternsToProjects.tsx @@ -13,26 +13,24 @@ interface AddInternsModalProps { export default function AddInternsModal({ isOpen, onClose, userId }: AddInternsModalProps) { const dialogRef = useRef(null); - - // Form States const [selectedUserId, setSelectedUserId] = useState(""); const [selectedProjectId, setSelectedProjectId] = useState(""); - - // Hooks const { assign, loading: assigning, error: assignError } = useAssignMember(); const { projects, loading: loadingProjects } = useProjects("active"); const { interns, loading: loadingInterns } = useAssignableInterns(); + console.log(projects) + useEffect(() => { const dialog = dialogRef.current; if (!dialog) return; if (isOpen) { dialog.showModal(); - document.body.style.overflow = "hidden"; + // document.body.style.overflow = "hidden"; } else { dialog.close(); - document.body.style.overflow = "unset"; + // document.body.style.overflow = "unset"; } const handleCancel = (e: Event) => { diff --git a/src/Pages/internify/mentor/components/CompleteProjectDialog.tsx b/src/Pages/internify/mentor/components/CompleteProjectDialog.tsx index 04a0fac..ba1907f 100644 --- a/src/Pages/internify/mentor/components/CompleteProjectDialog.tsx +++ b/src/Pages/internify/mentor/components/CompleteProjectDialog.tsx @@ -24,10 +24,10 @@ export default function CompleteProjectDialog({ if (isOpen) { dialog.showModal(); - document.body.style.overflow = "hidden"; + // document.body.style.overflow = "hidden"; } else { dialog.close(); - document.body.style.overflow = "unset"; + // document.body.style.overflow = "unset"; } const handleCancel = (e: Event) => { diff --git a/src/Pages/internify/mentor/components/CreateProjectDialog.tsx b/src/Pages/internify/mentor/components/CreateProjectDialog.tsx index 43f8058..12e1cfe 100644 --- a/src/Pages/internify/mentor/components/CreateProjectDialog.tsx +++ b/src/Pages/internify/mentor/components/CreateProjectDialog.tsx @@ -38,10 +38,8 @@ export default function CreateProjectModal({ isOpen, onClose, onSuccess }: Creat if (isOpen) { dialog.showModal(); - document.body.style.overflow = "hidden"; } else { dialog.close(); - document.body.style.overflow = "unset"; } const handleCancel = (e: Event) => { diff --git a/src/Pages/internify/mentor/components/EligibleInternTable.tsx b/src/Pages/internify/mentor/components/EligibleInternTable.tsx index 7dbc097..6c39373 100644 --- a/src/Pages/internify/mentor/components/EligibleInternTable.tsx +++ b/src/Pages/internify/mentor/components/EligibleInternTable.tsx @@ -171,7 +171,7 @@ export default function EligibleInternTable({ ).then(() => true).catch(() => false); setGenerating(false); - if (ok) navigate(`/mentor/certificates/${project.id}/result`); + if (ok) navigate(`/mentor/certificates/${project.slug}/result`); }; if (loading) { diff --git a/src/Pages/internify/mentor/components/HomeMentorProject.tsx b/src/Pages/internify/mentor/components/HomeMentorProject.tsx index d11d2ee..d5f3f18 100644 --- a/src/Pages/internify/mentor/components/HomeMentorProject.tsx +++ b/src/Pages/internify/mentor/components/HomeMentorProject.tsx @@ -4,6 +4,19 @@ import { Plus } from 'lucide-react'; import CreateProjectModal from './CreateProjectDialog'; import { useProjects } from '../../../../hooks/useProjects'; import { getProjectIcon } from '../../../../lib/ProjectIcons'; +import { RiFileList2Fill } from 'react-icons/ri'; + +function ProjectCardSkeleton() { + return ( +
+
+
+
+
+
+
+ ); +} const HomeMentorProject = () => { const navigate = useNavigate(); @@ -30,8 +43,23 @@ const HomeMentorProject = () => {
- {loading &&

Memuat...

} + {loading && ( + <> + {[...Array(2)].map((_, i) => ( + + ))} + + )} + {error &&

{error}

} + + {projects.length === 0 && !loading && ( +
+ +

Belum ada project

+
+ )} + {projects.map((project) => (
setCurrentPage((p) => Math.max(1, p - 1)); const handleNext = () => setCurrentPage((p) => Math.min(totalPages, p + 1)); - // Reset ke halaman 1 ketika filter berubah (melalui props interns) - // Gunakan useEffect agar reset terjadi saat props berubah - // eslint-disable-next-line react-hooks/exhaustive-deps - return (
@@ -67,7 +63,7 @@ export default function InternsTable({ {/* Table Body Iteration Rows */}
- {paginatedInterns.length > 0 ? ( + {paginatedInterns.length > 0 ? ( paginatedInterns.map((intern, index) => { const isUnassigned = !intern.projectName || intern.projectName === "Unassigned"; @@ -128,9 +124,13 @@ export default function InternsTable({
); }) + ) : totalInterns != 0 ? ( +
+ Tidak ada intern yang sesuai dengan kriteria +
) : ( -
- No interns found matching the filter criteria. +
+ Tidak ada intern yang terdaftar
)}
diff --git a/src/Pages/internify/mentor/components/SidebarMentor.tsx b/src/Pages/internify/mentor/components/SidebarMentor.tsx index 54dc3f4..03141ff 100644 --- a/src/Pages/internify/mentor/components/SidebarMentor.tsx +++ b/src/Pages/internify/mentor/components/SidebarMentor.tsx @@ -1,5 +1,6 @@ import { useNavigate, useLocation } from 'react-router-dom'; import { Home, FolderKanban, UsersRound, Medal, Settings, HelpCircle, LogOut, X } from 'lucide-react'; +import { FaUserTie } from "react-icons/fa"; const navItems = [ // belum kutambahin @@ -39,19 +40,21 @@ export default function SidebarMentor({ isOpen, closeSidebar }: SidebarProps) { return (