feat: update routing parameters, add header component, and refactor project details; enhance breadcrumb labels and task timeline

This commit is contained in:
Kemas M. Aryadary Rasyad
2026-06-16 11:47:47 +07:00
parent b7489e3be7
commit 8b20c11822
12 changed files with 318 additions and 90 deletions
+3 -4
View File
@@ -83,13 +83,12 @@ function App() {
</Route>
<Route path="/login-internify" element={<InternifyLogin />} />
{/* // Endpoints for intern-specific pages */}
<Route path="/intern" element={<InternLayout />}>
<Route index element={<HomeInternPage />} />
<Route path="projects" element={<ProjectsPage />} />
<Route path="certificates" element={<SertificatePage />} />
<Route path="projects/:id" element={<ProjectDetailPage />} />
<Route path="tasks/:id/submit" element={<TaskSubmission />} />
<Route path="projects/:slug" element={<ProjectDetailPage />} />
<Route path="projects/:slug/submit" element={<TaskSubmission />} />
<Route path="faq" element={<FAQPage />} />
<Route path="notifications" element={<NotificationList />} />
<Route path="settings" element={<SettingsContent />} />
@@ -100,7 +99,7 @@ function App() {
<Route path="projects" element={<MentorProjectsPage />} />
<Route path="certificates" element={<MentorCertificatePage />} />
<Route path="intern" element={<MentorInternPage />} />
<Route path="projects/:id" element={<MentorDetailProject />} />
<Route path="projects/:slug" element={<MentorDetailProject />} />
<Route path="faq" element={<FAQPage />} />
<Route path="notifications" element={<NotificationList />} />
<Route path="settings" element={<SettingsContent />} />
@@ -1,11 +1,11 @@
import { Bell, Menu } from "lucide-react"
import humiclogo from "../../../../assets/logo.png";
import humiclogo from "../assets/logo.png";
import { useNavigate } from "react-router-dom";
export default function Header({ toggleSidebar }: { toggleSidebar: () => void }) {
const nav = useNavigate();
return (
<header className='sticky top-0 flex w-full justify-between items-center py-4 px-12 bg-white border-b border-gray-300 z-10'>
<header className='sticky top-0 flex w-full justify-between items-center py-2 px-12 bg-white border-b border-gray-300 z-10'>
<div className='flex items-center gap-3'>
{/* Tombol Burger Menu: Hanya muncul di layar < md */}
<button
@@ -16,7 +16,7 @@ export default function Header({ toggleSidebar }: { toggleSidebar: () => void })
</button>
<button onClick={()=>nav('/intern')}>
<img src={humiclogo} alt="Humic Logo" className="w-[130px] cursor-pointer" />
<img src={humiclogo} alt="Humic Logo" className="w-[100px] cursor-pointer" />
</button>
</div>
+4 -6
View File
@@ -6,19 +6,17 @@ const breadcrumbRoutes = [
// --- AREA INTERN ---
{ path: '/intern', breadcrumb: 'Home' },
{ path: '/intern/projects', breadcrumb: 'Projects' },
{ path: '/intern/projects/:id', breadcrumb: 'Detail Project' }, // Otomatis handle ID dinamis
{ path: '/intern/projects/:id', breadcrumb: 'Forum' },
{ path: '/intern/certificates', breadcrumb: 'Certificates' },
{ path: '/intern/tasks', breadcrumb: 'Tasks' },
{ path: '/intern/tasks/:id', breadcrumb: 'Detail Task' },
{ path: '/intern/tasks/:id/submit', breadcrumb: 'Submit Task' },
{ path: '/intern/projects/:id/submit', breadcrumb: 'Submit Task' },
{ path: '/intern/faq', breadcrumb: 'FAQ' },
{ path: '/intern/notifications', breadcrumb: 'Notifications' },
{ path: '/intern/settings', breadcrumb: 'Settings' },
// --- AREA MENTOR / ADMIN ---
{ path: '/mentor', breadcrumb: 'Home' },
{ path: '/mentor/projects', breadcrumb: 'Managed Projects' },
{ path: '/mentor/projects/:id', breadcrumb: 'Project Overview' },
{ path: '/mentor/projects', breadcrumb: 'List Projects' },
{ path: '/mentor/projects/:id', breadcrumb: 'Forum' },
{ path: '/mentor/certificates', breadcrumb: 'Approve Certificates' },
{ path: '/mentor/intern', breadcrumb: 'List Intern' },
{ path: '/mentor/faq', breadcrumb: 'FAQ' },
@@ -31,7 +31,7 @@ export default function ParticipantsTab({ project }: { project: Project }) {
>
<div className="flex items-center gap-3">
<span className=" text-sm text-[#333]">
{intern.email}
{intern.name}
</span>
</div>
<span className=" text-[13px] text-[#555] font-semibold">
@@ -1,25 +1,15 @@
import { useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { useParams } from 'react-router-dom';
import { mockProjects } from '../../../lib/mockData';
import ForumTab from './ForumTab';
import ParticipantsTab from './ParticipantTab';
import { ChevronLeft } from 'lucide-react';
// ─── ProjectDetailPage ─────────────────────────────────────────────────────────
export default function ProjectDetailPage() {
const { id } = useParams<{ id: string }>();
const navigate = useNavigate();
const { slug } = useParams<{ slug: string }>();
const [activeTab, setActiveTab] = useState<'forum' | 'participants'>('forum');
const handleBack = () => {
if (window.history.length > 1) {
navigate(-1);
} else {
navigate('/intern/projects');
}
};
const project = mockProjects.find((p) => p.id === Number(id));
const project = mockProjects.find((p) => p.name === slug);
if (!project) {
return (
@@ -1,58 +1,44 @@
import type { TaskTimelineProps } from "../../../../lib/mockDeadline"
import { defaultTasks } from "../../../../lib/mockDeadline"
import { DEADLINES } from "../../../../lib/mockDeadline";
import type { Deadline } from "../../../../lib/mockDeadline";
const TaskTimeline = ({ tasks = defaultTasks }: TaskTimelineProps) => {
const today = new Date()
const TaskTimeline = () => {
const today = new Date();
const isOverdue = (tanggal: string) => {
const deadline = new Date(tanggal)
return deadline < today
}
const isOverdue = (date: Date) => {
return date < today;
};
const upcomingIndex = tasks.findIndex(
task => new Date(task.tanggal) >= today
)
return (
<div>
<h2 className="text-base font-semibold text-gray-800 mb-5">Task Timeline</h2>
<div className="relative">
{/* Vertical line */}
<div className="absolute left-1.5 top-2 bottom-2 w-px bg-gray-200" />
<div className="absolute left-1 top-2 bottom-2 w-px bg-gray-200" />
<ul className="flex flex-col gap-5">
{tasks.map((task, index) => {
const overdue = isOverdue(task.tanggal)
const isRedDot = overdue || index === upcomingIndex
{DEADLINES.map((item: Deadline, index: number) => {
const overdue = isOverdue(item.date);
return (
<li key={index} className="flex gap-4 pl-0">
{/* Dot */}
<div className="relative flex-shrink-0 mt-1">
<span
className={`block w-3 h-3 rounded-full border-2 z-10 relative
${isRedDot
? 'bg-red-800 '
: 'bg-gray-400'
className={`block w-2 h-2 rounded-full z-10 relative ${
overdue ? "bg-red-700" : "bg-gray-400"
}`}
/>
</div>
{/* Content */}
<div className="flex flex-col gap-0.5 min-w-0">
<p className="text-[10px] font-semibold text-gray-400 uppercase tracking-widest">
{task.tanggal}
{item.date.toLocaleDateString("id-ID")}
</p>
<p className="text-sm font-semibold text-gray-800">{task.title}</p>
<p className="text-xs text-gray-500">{task.description}</p>
<p className="text-sm font-semibold text-gray-800">{item.label}</p>
<p className="text-xs text-gray-500">{item.time}</p>
</div>
</li>
)
);
})}
</ul>
</div>
</div>
)
}
);
};
export default TaskTimeline
export default TaskTimeline;
+1 -1
View File
@@ -1,6 +1,6 @@
import { Outlet } from "react-router-dom";
import { useState } from "react";
import Header from './mentor/components/Header';
import Header from '../../Layout/Header-internify';
import SidebarIntern from './intern/components/SidebarIntern';
import SidebarMentor from './mentor/components/SidebarMentor';
import FooterIntern from '../../Layout/Footer-Internify';
+2 -2
View File
@@ -8,7 +8,7 @@ import TaskTab from './TaskTab';
// ─── ProjectDetailPage ─────────────────────────────────────────────────────────
export default function ProjectDetailPage() {
const { id } = useParams<{ id: string }>();
const { slug } = useParams<{ slug: string }>();
const navigate = useNavigate();
const [activeTab, setActiveTab] = useState<'forum' | 'participants' | 'Task'>('forum');
@@ -20,7 +20,7 @@ export default function ProjectDetailPage() {
}
};
const project = mockProjects.find((p) => p.id === Number(id));
const project = mockProjects.find((p) => p.name === slug);
if (!project) {
return (
+1 -1
View File
@@ -101,7 +101,7 @@ export const mockProjects: Project[] = [
{ name: "Dewa Aditya", role: "UI/UX", email: "jonathankristina@gmail.com" },
{ name: "Eka Pratiwi", role: "UI/UX", email: "jonathankristina@gmail.com" },
],
mentor: "jonathankristina@gmail.com",
mentor: "jonathankristina",
progress: 12,
tasksDone: 1,
tasksTotal: 8,
+17 -24
View File
@@ -1,27 +1,20 @@
interface Task {
tanggal: string
title: string
description: string
export interface Deadline {
date: Date;
label: string;
time: string;
}
export interface TaskTimelineProps {
tasks?: Task[]
}
export const DEADLINES: Deadline[] = [
{
date: new Date(2025, 4, 4),
label: "Laporan Progress 1 Internify – 4 Juni",
time: "23:59 PM",
},
{
date: new Date(2026, 5, 5),
label: "Laporan Progress 2 Internify - 5 May",
time: "23:59 PM",
},
];
export const defaultTasks: Task[] = [
{
tanggal: '2026-05-14T23:59:00',
title: 'Laporan tugas 1',
description: 'Berisi design uiux, dan user flow dari aplikasi internify',
},
{
tanggal: '2026-05-15T23:59:00',
title: 'Laporan tugas 1',
description: 'Berisi design uiux, dan user flow dari aplikasi internify',
},
{
tanggal: '2026-06-20T21:00:00',
title: 'Laporan tugas 1',
description: 'Berisi design uiux, dan user flow dari aplikasi internify',
},
]
export const deadlineDates: Date[] = DEADLINES.map((d) => d.date);
+59
View File
@@ -0,0 +1,59 @@
export interface InternData {
id: number;
name: string;
email: string;
projectName: string;
role: string;
avatar: string;
}
export const mockInternsData: InternData[] = [
{
id: 1,
name: "Alex Rivera",
email: "alex.rivera@internify.edu",
projectName: "Internify Project",
role: "UI/UX",
avatar: "https://images.unsplash.com/photo-1534528741775-53994a69daeb?auto=format&fit=crop&w=100&q=80",
},
{
id: 2,
name: "Sarah Jenkins",
email: "s.jenkins@internify.edu",
projectName: "Internify Project",
role: "Frontend",
avatar: "https://images.unsplash.com/photo-1494790108377-be9c29b29330?auto=format&fit=crop&w=100&q=80",
},
{
id: 3,
name: "Michael Chen",
email: "m.chen@internify.edu",
projectName: "MBG Project",
role: "Backend",
avatar: "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?auto=format&fit=crop&w=100&q=80",
},
{
id: 4,
name: "Elena Rodriguez",
email: "e.rod@internify.edu",
projectName: "Unassigned",
role: "-",
avatar: "https://images.unsplash.com/photo-1438761681033-6461ffad8d80?auto=format&fit=crop&w=100&q=80",
},
{
id: 5,
name: "Alex Rivera",
email: "alex.rivera@internify.edu",
projectName: "Internify Project",
role: "UI/UX",
avatar: "",
},
{
id: 6,
name: "Sarah Jenkins",
email: "s.jenkins@internify.edu",
projectName: "Internify Project",
role: "Backend",
avatar: "",
},
];
+203
View File
@@ -0,0 +1,203 @@
import type { Deadline } from "./mockDeadline";
export interface Task {
id: number;
title: string;
description: string;
deadline: Deadline;
isSubmitted: boolean;
submittedCount: number;
totalInterns: number;
}
export interface Intern {
name: string;
role: string;
email: string;
}
export interface Project {
id: number;
name: string;
description: string;
tasks: Task[];
interns: Intern[];
mentor: string;
avatarUrl?: string;
thumbnailUrl?: string;
progress: number;
tasksDone: number;
tasksTotal: number;
}
export const mockProjects: Project[] = [
{
id: 1,
name: "Internify Project",
description:
"Internify merupakan website Human Capital (HUMC) yang dirancang sebagai platform pendaftaran dan pengelolaan program magang secara digital. Sistem ini dibuat untuk membantu proses rekrutmen peserta magang mulai dari pendaftaran, seleksi, hingga monitoring aktivitas peserta selama program berlangsung.\n\nFokus utama dari pengembangan Internify adalah pembuatan Learning Management System (LMS) yang digunakan untuk mendukung kegiatan pembelajaran dan pengelolaan peserta magang. LMS pada Internify menyediakan fitur seperti akses materi pembelajaran, pengumpulan tugas, monitoring progress peserta, absensi, sertifikat, hingga komunikasi antara mentor dan peserta magang.",
tasks: [
{
id: 1,
title: "Laporan tugas",
description: "Berisi design ulux, dan user flow dari aplikasi internify",
deadline: { date: new Date(2026, 4, 1), label: "Laporan tugas – 1 Mei", time: "23:59 PM" },
submittedCount: 6,
totalInterns: 6,
isSubmitted: true,
},
{
id: 2,
title: "Laporan tugas 1",
description: "Berisi design ulux, dan user flow dari aplikasi internify",
deadline: { date: new Date(2026, 4, 14), label: "Laporan tugas 1 – 14 Mei", time: "23:59 PM" },
submittedCount: 2,
totalInterns: 6,
isSubmitted: false,
},
{
id: 3,
title: "Laporan tugas 2",
description: "Berisi design ulux, dan user flow dari aplikasi internify",
deadline: { date: new Date(2026, 4, 28), label: "Laporan tugas 2 – 28 Mei", time: "23:59 PM" },
submittedCount: 3,
totalInterns: 6,
isSubmitted: false,
},
{
id: 4,
title: "Laporan tugas 3",
description: "Berisi wireframe halaman dashboard aplikasi internify",
deadline: { date: new Date(2026, 5, 5), label: "Laporan tugas 3 – 5 Jun", time: "23:59 PM" },
submittedCount: 0,
totalInterns: 6,
isSubmitted: false,
},
],
interns: [
{ name: "Jonathan Kristina", role: "Backend", email: "jonathankristina@gmail.com" },
{ name: "Anisa Rahmawati", role: "Backend", email: "jonathankristina@gmail.com" },
{ name: "Budi Santoso", role: "Frontend", email: "jonathankristina@gmail.com" },
{ name: "Cindy Permata", role: "Frontend", email: "jonathankristina@gmail.com" },
{ name: "Dewa Aditya", role: "UI/UX", email: "jonathankristina@gmail.com" },
{ name: "Eka Pratiwi", role: "UI/UX", email: "jonathankristina@gmail.com" },
],
mentor: "jonathankristina@gmail.com",
progress: 12,
tasksDone: 1,
tasksTotal: 8,
},
{
id: 2,
name: "Rekrutmen Digital Platform",
description:
"Platform digital yang memudahkan proses rekrutmen karyawan baru secara end-to-end. Mulai dari posting lowongan, screening CV otomatis, penjadwalan wawancara, hingga onboarding karyawan baru.\n\nSistem ini mengintegrasikan berbagai modul HR seperti manajemen data kandidat, penilaian kompetensi berbasis AI, dan dashboard analytics untuk tim HR dalam mengambil keputusan rekrutmen yang lebih efektif dan efisien.",
tasks: [
{
id: 1,
title: "Analisis Kebutuhan Sistem",
description: "Dokumentasi kebutuhan fungsional dan non-fungsional platform rekrutmen",
deadline: { date: new Date(2026, 4, 5), label: "Analisis Kebutuhan – 5 Mei", time: "23:59 PM" },
submittedCount: 6,
totalInterns: 6,
isSubmitted: true,
},
{
id: 2,
title: "Desain Database Schema",
description: "Rancangan struktur database untuk kandidat, lowongan, dan proses seleksi",
deadline: { date: new Date(2026, 4, 10), label: "Desain Database – 10 Mei", time: "23:59 PM" },
submittedCount: 6,
totalInterns: 6,
isSubmitted: true,
},
{
id: 3,
title: "Implementasi Auth Module",
description: "Pengembangan sistem autentikasi dan otorisasi multi-role",
deadline: { date: new Date(2026, 4, 18), label: "Auth Module – 18 Mei", time: "23:59 PM" },
submittedCount: 3,
totalInterns: 6,
isSubmitted: true,
},
{
id: 4,
title: "Modul Posting Lowongan",
description: "Fitur buat, edit, dan publish lowongan pekerjaan",
deadline: { date: new Date(2026, 4, 25), label: "Posting Lowongan – 25 Mei", time: "23:59 PM" },
submittedCount: 0,
totalInterns: 6,
isSubmitted: false,
},
],
interns: [
{ name: "Fajar Nugroho", role: "Backend", email: "fajarnugroho@gmail.com" },
{ name: "Gita Melinda", role: "Backend", email: "gitamelinda@gmail.com" },
{ name: "Hendra Wijaya", role: "Frontend", email: "hendrawijaya@gmail.com" },
{ name: "Indira Sari", role: "Frontend", email: "indirasari@gmail.com" },
{ name: "Joko Purnomo", role: "UI/UX", email: "jokopurnomo@gmail.com" },
{ name: "Kartika Dewi", role: "QA", email: "kartikadewi@gmail.com" },
{ name: "Lukman Hakim", role: "QA", email: "lukmanhakim@gmail.com" },
],
mentor: "seniormHR@company.com",
progress: 37,
tasksDone: 3,
tasksTotal: 8,
},
{
id: 3,
name: "E-Commerce Analytics Dashboard",
description:
"Dashboard analitik komprehensif untuk platform e-commerce yang menyajikan insight real-time tentang performa penjualan, perilaku pelanggan, dan efektivitas kampanye marketing.\n\nProyek ini mencakup pengembangan pipeline data dari berbagai sumber transaksi, visualisasi interaktif menggunakan chart library modern, serta fitur export laporan dalam berbagai format untuk kebutuhan manajemen bisnis.",
tasks: [
{
id: 1,
title: "Setup Pipeline Data",
description: "Konfigurasi ETL pipeline dari database transaksi ke data warehouse",
deadline: { date: new Date(2026, 4, 3), label: "Setup Pipeline – 3 Mei", time: "23:59 PM" },
submittedCount: 6,
totalInterns: 6,
isSubmitted: true,
},
{
id: 2,
title: "Desain Komponen Chart",
description: "Pembuatan reusable chart component untuk berbagai tipe visualisasi data",
deadline: { date: new Date(2026, 4, 12), label: "Komponen Chart – 12 Mei", time: "23:59 PM" },
submittedCount: 6,
totalInterns: 6,
isSubmitted: true,
},
{
id: 3,
title: "Dashboard Overview Page",
description: "Halaman utama dengan ringkasan KPI dan metric utama bisnis",
deadline: { date: new Date(2026, 4, 20), label: "Overview Page – 20 Mei", time: "23:59 PM" },
submittedCount: 0,
totalInterns: 6,
isSubmitted: false,
},
{
id: 4,
title: "Laporan Penjualan",
description: "Modul analitik penjualan dengan filter periode dan kategori produk",
deadline: { date: new Date(2026, 4, 28), label: "Laporan Penjualan – 28 Mei", time: "23:59 PM" },
submittedCount: 0,
totalInterns: 6,
isSubmitted: false,
},
],
interns: [
{ name: "Maya Anggraeni", role: "Backend", email: "mayaanggraeni@gmail.com" },
{ name: "Nando Pratama", role: "Backend", email: "nandopratama@gmail.com" },
{ name: "Olivia Susanto", role: "Frontend", email: "oliviasusanto@gmail.com" },
{ name: "Pandu Kusuma", role: "Frontend", email: "pandukukusuma@gmail.com" },
{ name: "Qori Ramadhan", role: "Data Analyst", email: "qoriramadhan@gmail.com" },
{ name: "Rini Handayani", role: "UI/UX", email: "rinihandayani@gmail.com" },
],
mentor: "leadanalyst@company.com",
progress: 50,
tasksDone: 4,
tasksTotal: 8,
},
];