feat: enhance routing and UI components for intern and mentor sections

This commit is contained in:
Kemas M. Aryadary Rasyad
2026-07-16 12:05:50 +07:00
parent 5689898f33
commit 99082efbdb
8 changed files with 90 additions and 57 deletions
+8 -3
View File
@@ -1,4 +1,5 @@
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import { lazy, Suspense } from "react";
import { Toaster } from "react-hot-toast";
import Landing from "./Pages/Landing";
import AboutUs from "./Pages/AboutUs";
@@ -56,6 +57,10 @@ import NotFoundPage from "./Pages/NotFoundPage";
import InternCertificateDetail from "./Pages/Internify/intern/InternCertDetail";
import VerificationCert from "./Pages/Internify/VerificationCert";
const InternCertificatePage = lazy(() => import("./Pages/Internify/intern/InternCertDetail"));
const MentorCertForm = lazy(() => import("./Pages/Internify/mentor/CertificateForm"));
const MentorCertResult = lazy(() => import("./Pages/Internify/mentor/CertificateResult"));
function App() {
return (
<>
@@ -111,7 +116,7 @@ function App() {
<Route index element={<HomeInternPage />} />
<Route path="projects" element={<ProjectsPage />} />
<Route path="certificates" element={<SertificateList />} />
<Route path="certificates/:slug" element={<InternCertificateDetail />} />
<Route path="certificates/:slug" element={<InternCertificatePage />} />
<Route path="projects/:slug" element={<ProjectDetailPage />} />
<Route path="projects/:slug/:taskSlug" element={<TaskSubmission />} />
<Route path="faq" element={<FAQPage />} />
@@ -126,8 +131,8 @@ function App() {
<Route index element={<HomeMentorPage />} />
<Route path="projects" element={<MentorProjectsPage />} />
<Route path="certificates" element={<MentorCertificatePage />} />
<Route path="certificates/:slug" element={<CertificateDetail />} />
<Route path="certificates/:slug/result" element={<CertificateResult />} />
<Route path="certificates/:slug" element={<MentorCertForm />} />
<Route path="certificates/:slug/result" element={<MentorCertResult />} />
<Route path="intern" element={<MentorInternPage />} />
<Route path="projects/:slug" element={<MentorDetailProject />} />
<Route path="projects/:slug/:taskSlug" element={<MentorProjectsDetailPage />} />
+55 -43
View File
@@ -1,35 +1,36 @@
import { useNavigate } from 'react-router-dom';
import { Link } from 'react-router-dom';
import { FaInstagram, FaLinkedin, FaRegEnvelope } from "react-icons/fa";
import logoHumic from "../assets/logo.png"
import logoHumic from "../assets/logo.png";
const footerLinks = {
Internship: [
{ label: "Beranda", path: "/" },
{ label: "Tentang", path: "/about-us" },
{ label: "Life At Humic", path: "/our-developer" },
],
External: [
{ label: "Humic Website", path: "https://humic.telkomuniversity.ac.id/" },
{ label: "Our Activity", path: "/activity" },
{ label: "Life At Humic", path: "/life-humic" },
{ label: "Our Developer", path: "/developer" },
],
};
const internshipLinks = [
{ label: "Beranda", path: "/" },
{ label: "Tentang", path: "/about-us" },
{ label: "Life At Humic", path: "/our-developer" },
];
const externalLinks = [
{ label: "Humic Website", href: "https://humic.telkomuniversity.ac.id/" },
{ label: "Our Activity", href: "https://humic.telkomuniversity.ac.id/humicinmedia/" },
{ label: "Life At Humic", href: "#" },
{ label: "Our Developer", href: "#" },
];
const socials = [
{ label: "Instagram", icon: FaInstagram, href: "https://www.instagram.com/humicengineering/" },
{ label: "LinkedIn", icon: FaLinkedin, href: "https://www.linkedin.com/company/humic-engineering/" },
{ label: "Email", icon: FaRegEnvelope, href: "https://humic.telkomuniversity.ac.id/" },
{ label: "Email", icon: FaRegEnvelope, href: "#" },
];
export function FooterIntern() {
const navigate = useNavigate();
return (
<footer className="bg-white border-t border-gray-300 px-14 py-8 flex gap-12 flex-wrap">
{/* Brand info */}
<div className="flex flex-col gap-2 flex-shrink-0 w-70">
<div className="flex flex-col gap-2 flex-shrink-0 w-70 items-start">
<img
src={logoHumic}
alt="logo humic"
className="w-[160px]"
/>
<address className="text-xs text-gray-800 leading-relaxed not-italic">
Gedung Kultubai Selatan, Blok F
Jl. Telekomunikasi, Terusan Buah Batu Bandung
@@ -53,32 +54,43 @@ export function FooterIntern() {
{/* Link columns */}
<div className="flex gap-12 flex-1 justify-end flex-wrap">
{Object.entries(footerLinks).map(([title, links]) => (
<div key={title} className="flex flex-col gap-2 min-w-[110px]">
<p className="text-[13px] font-bold text-gray-900">{title}</p>
<ul className="flex flex-col gap-0.5">
{links.map(({ label, path }) => (
<li key={label}>
<button
onClick={() => navigate(path)}
className="text-[12.5px] text-gray-600 hover:text-red-800 transition-colors text-left bg-transparent border-none p-0 cursor-pointer"
>
{label}
</button>
</li>
))}
</ul>
</div>
))}
<div className="flex flex-col gap-2 min-w-[140px]">
<p className="text-[13px] font-bold text-gray-900">Internship</p>
<ul className="flex flex-col gap-0.5">
{internshipLinks.map(({ label, path }) => (
<li key={label}>
<Link
to={path}
className="text-[12.5px] text-gray-600 hover:text-red-800 transition-colors"
>
{label}
</Link>
</li>
))}
</ul>
</div>
<div className="flex flex-col gap-2 min-w-[140px]">
<p className="text-[13px] font-bold text-gray-900">Eksternal</p>
<ul className="flex flex-col gap-0.5">
{externalLinks.map(({ label, href }) => (
<li key={label}>
<a
href={href}
target="_blank"
rel="noopener noreferrer"
className="text-[12.5px] text-gray-600 hover:text-red-800 transition-colors"
>
{label}
</a>
</li>
))}
</ul>
</div>
</div>
<img
src={logoHumic}
alt="logo humic"
className='w-[110px] h-[68px]'
/>
</footer>
);
}
export default FooterIntern;
export default FooterIntern
+10 -5
View File
@@ -1,5 +1,5 @@
import { Bell, Menu } from "lucide-react"
import { useState } from "react";
import { useEffect, useState } from "react";
import humiclogo from "../assets/logo.png";
import { useNavigate } from "react-router-dom";
import { useCurrentUser, resolveFileUrl, getInitials } from "../hooks/useUser";
@@ -11,6 +11,11 @@ export default function Header({ toggleSidebar }: { toggleSidebar: () => void })
const [imageError, setImageError] = useState(false);
const { unreadCount } = useNotifications();
const photoUrl = resolveFileUrl(user?.profile_picture);
useEffect(() => {
setImageError(false);
}, [photoUrl]);
const displayName = user?.full_name?.trim() || [user?.nama_depan, user?.nama_belakang].filter(Boolean).join(" ") || "";
const initials = getInitials(displayName);
@@ -27,7 +32,7 @@ export default function Header({ toggleSidebar }: { toggleSidebar: () => void })
</button>
<button>
<img src={humiclogo} alt="Humic Logo" className="w-[100px] cursor-pointer" />
<img src={humiclogo} alt="Humic Logo" className="w-[120px] cursor-pointer" />
</button>
</div>
@@ -35,7 +40,7 @@ export default function Header({ toggleSidebar }: { toggleSidebar: () => void })
<button onClick={() => nav("notifications")} className="relative">
<Bell className="w-5 h-5 hover:text-red-800"/>
{unreadCount > 0 && (
<span className="absolute -top-1 -right-1 w-2.5 h-2.5 bg-red-600 rounded-full border-2 border-white" />
<span className="absolute -top-1 -right-0.5 w-2.5 h-2.5 bg-red-600 rounded-full border-2 border-white" />
)}
</button>
@@ -46,8 +51,8 @@ export default function Header({ toggleSidebar }: { toggleSidebar: () => void })
>
{hasPhoto ? (
<img
src={`${import.meta.env.VITE_API_BASE_URL}${user?.profile_picture ?? ""}`}
alt={user?.full_name ?? "Profile"}
src={photoUrl!}
alt={displayName || "Profile"}
className="w-full h-full object-cover"
onError={() => setImageError(true)}
/>
+1 -1
View File
@@ -82,7 +82,7 @@ const Navbar = () => {
}}
className="block w-full text-left hover:text-red-600 transition"
>
Internify
Portal LMS
</button>
</div>
</div>
@@ -93,7 +93,7 @@ export default function SidebarIntern({ isOpen, closeSidebar }: SidebarProps) {
PREFERENCES
</p>
<nav className="flex flex-col gap-0.5">
{prefItems.map(({ label, icon: Icon, path, danger, isLogout }) => {
{prefItems.map(({ label, icon: Icon, path, isLogout }) => {
const isActive = location.pathname === path;
return (
<button
@@ -42,7 +42,7 @@ export default function SubmitStatusTable({
: "Assignment was submitted early";
const rows = [
{ label: "Submission status", value: "Submitted for grading" },
{ label: "Submission status", value: "Submitted" },
{ label: "Time Remaining", value: timeLabel },
];
@@ -23,12 +23,18 @@ export default function MentorProjectsPage() {
refetch();
};
const STATUS_ORDER: Record<string, number> = {
active: 0,
completed: 1,
archived: 2,
};
const renderContent = () => {
if (loading) {
return (
<div className="flex flex-row gap-3 w-full">
{[...Array(4)].map((_, i) => (
{[...Array(4)].map((_) => (
<MentorProjectCardSkeleton />
))}
</div>
@@ -48,7 +54,12 @@ export default function MentorProjectsPage() {
);
}
return projects.map(project => (
const sorted = [...projects].sort(
(a, b) =>
(STATUS_ORDER[a.status] ?? 99) - (STATUS_ORDER[b.status] ?? 99)
);
return sorted.map(project => (
<MentorProjectCard
key={project.id}
{...project}
@@ -1,4 +1,4 @@
import { useRef, useEffect, useState, useCallback } from "react";
import { useRef, useEffect, useState } from "react";
import { X } from "lucide-react";
import { PROJECT_ICON_MAP, PROJECT_ICON_CODES } from "../../../../lib/ProjectIcons";
import { LuSendHorizontal, LuMail } from "react-icons/lu";