feat: add breadcrumbs component and integrate with layout; update dependencies and refactor various pages
This commit is contained in:
@@ -3,10 +3,9 @@ import HomeProjectList from "./components/HomeProjectList";
|
||||
import HomeCalendar from "./components/HomeCalendar";
|
||||
import TaskTimeline from "./components/TaskTimeline";
|
||||
|
||||
// PR: tambahin breadcrumb
|
||||
export default function HomeInternPage() {
|
||||
return (
|
||||
<div className="container flex-1">
|
||||
<div className="flex-1">
|
||||
<HomeHeader />
|
||||
<div className="flex flex-col gap-5">
|
||||
<div className="grid grid-cols-2 gap-5">
|
||||
|
||||
@@ -36,20 +36,8 @@ export default function ProjectDetailPage() {
|
||||
}`;
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
<div className="">
|
||||
<div>
|
||||
|
||||
{/* Back Button */}
|
||||
<button
|
||||
onClick={handleBack}
|
||||
className="flex items-center gap-2 mb-6 text-sm font-medium text-[#666] hover:text-[#C0392B] transition-colors duration-150 group"
|
||||
>
|
||||
<span className='flex flex-row gap-2'>
|
||||
<ChevronLeft width={20} height={20} className="transition-transform duration-150 group-hover:-translate-x-1" />
|
||||
Kembali ke Daftar Project
|
||||
</span>
|
||||
</button>
|
||||
|
||||
{/* Tabs */}
|
||||
<div className="flex mb-4">
|
||||
<button className={tabClass('forum')} onClick={() => setActiveTab('forum')}>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { mockProjects } from '../../../lib/mockData'
|
||||
|
||||
export default function ProjectsPage() {
|
||||
return (
|
||||
<div className="container">
|
||||
<div className="">
|
||||
<div className="flex flex-col gap-1 mb-6">
|
||||
<span className="page-title">List Projects</span>
|
||||
<h1 className="page-title-desc">View assigned projects and track their progress.</h1>
|
||||
|
||||
@@ -6,7 +6,7 @@ const SertificatePage = () => {
|
||||
const data = certificateData; // ganti isReceived di object atas
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
<div className="">
|
||||
{/* Header */}
|
||||
<div className="mb-12 flex flex-col gap-1">
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ export default function TaskSubmission() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container space-y-4">
|
||||
<div className="space-y-4">
|
||||
{/* Header Card */}
|
||||
<div className="w-full bg-white rounded-2xl border border-gray-100 p-6 shadow-[0_4px_20px_rgba(0,0,0,0.01)]">
|
||||
<div className="flex justify-between items-start">
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Lock, Clock } from "lucide-react";
|
||||
|
||||
export default function CertificateNotAvailable({ progress, remainingTasks }: { progress: number; remainingTasks: number }) {
|
||||
return (
|
||||
<div className="bg-white w-full rounded-2xl border border-gray-100 shadow-sm py-16 p-23 flex flex-col items-center text-center max-w-3/5 mx-auto z-10">
|
||||
<div className="bg-white w-full rounded-2xl border border-gray-100 shadow-sm py-16 p-23 flex flex-col items-center text-center md:max-w-3/5 mx-auto z-10">
|
||||
{/* Icon */}
|
||||
<div className="relative mb-6">
|
||||
<div className=" w-33 h-33 border-2 border-dashed border-gray-600 rounded-xl flex justify-center items-center">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState } from 'react'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { ChevronLeft, ChevronRight } from 'lucide-react'
|
||||
|
||||
interface Deadline {
|
||||
@@ -9,8 +9,7 @@ interface Deadline {
|
||||
}
|
||||
|
||||
const DEADLINES: Deadline[] = [
|
||||
{ date: 4, month: 4, year: 2025, label: 'Laporan Progress 1 Internify– 4 May' },
|
||||
{ date: 5, month: 4, year: 2025, label: 'Laporan Progress 1 Internify– 4 May' },
|
||||
{ date: 30, month: 6, year: 2026, label: 'Tenggat Proyek Internify Internify - 4 May' },
|
||||
]
|
||||
|
||||
const DAY_LABELS = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']
|
||||
@@ -20,7 +19,7 @@ const MONTH_NAMES = [
|
||||
]
|
||||
|
||||
function getDaysInMonth(year: number, month: number) {
|
||||
return new Date(year, month + 1, 0).getDate()
|
||||
return new Date(year, month+1, 0).getDate()
|
||||
}
|
||||
|
||||
function getFirstDayOfMonth(year: number, month: number) {
|
||||
@@ -28,9 +27,18 @@ function getFirstDayOfMonth(year: number, month: number) {
|
||||
}
|
||||
|
||||
const HomeCalendar = () => {
|
||||
const today = new Date()
|
||||
const [currentYear, setCurrentYear] = useState(2025)
|
||||
const [currentMonth, setCurrentMonth] = useState(4) // May = 4 (0-indexed)
|
||||
const [today, setToday] = useState(new Date())
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setToday(new Date())
|
||||
}, 60 * 1000) // update tiap 1 menit
|
||||
|
||||
return () => clearInterval(interval)
|
||||
}, [])
|
||||
|
||||
const [currentYear, setCurrentYear] = useState(today.getFullYear())
|
||||
const [currentMonth, setCurrentMonth] = useState(today.getMonth())
|
||||
|
||||
const daysInMonth = getDaysInMonth(currentYear, currentMonth)
|
||||
const firstDay = getFirstDayOfMonth(currentYear, currentMonth)
|
||||
@@ -59,7 +67,7 @@ const HomeCalendar = () => {
|
||||
currentYear === today.getFullYear()
|
||||
|
||||
const hasDeadline = (day: number) =>
|
||||
DEADLINES.some(d => d.date === day && d.month === currentMonth && d.year === currentYear)
|
||||
DEADLINES.some(d => d.date === day && d.month - 1 === currentMonth && d.year === currentYear)
|
||||
|
||||
const upcomingDeadlines = DEADLINES.filter(
|
||||
d => d.month === currentMonth && d.year === currentYear
|
||||
@@ -129,7 +137,7 @@ const HomeCalendar = () => {
|
||||
{currentDays.map(day => (
|
||||
<div key={day} className="flex justify-center items-center py-1 px-4">
|
||||
{isToday(day) ? (
|
||||
<span className="w-8 h-7 flex items-center justify-center rounded bg-red-800 text-white text-xs font-semibold">
|
||||
<span className="w-8 h-7 flex items-center justify-center border-b-2 border-red-800 text-xs font-semibold">
|
||||
{day}
|
||||
</span>
|
||||
) : (
|
||||
@@ -171,7 +179,7 @@ const HomeCalendar = () => {
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<p className="text-sm text-gray-400">No upcoming deadlines this month.</p>
|
||||
<p className="text-[11px] text-gray-400">No upcoming deadlines this month.</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,39 +1,17 @@
|
||||
interface Task {
|
||||
tanggal: string
|
||||
title: string
|
||||
description: string
|
||||
}
|
||||
|
||||
interface TaskTimelineProps {
|
||||
tasks?: Task[]
|
||||
}
|
||||
|
||||
const defaultTasks: Task[] = [
|
||||
{
|
||||
tanggal: 'MAY 14, 2026 AT 23:59 PM',
|
||||
title: 'Laporan tugas 1',
|
||||
description: 'Berisi design uiux, dan user flow dari aplikasi internify',
|
||||
},
|
||||
{
|
||||
tanggal: 'MAY 15, 2026 AT 23:59 PM',
|
||||
title: 'Laporan tugas 1',
|
||||
description: 'Berisi design uiux, dan user flow dari aplikasi internify',
|
||||
},
|
||||
{
|
||||
tanggal: 'MAY 16, 2026 AT 23:59 PM',
|
||||
title: 'Laporan tugas 1',
|
||||
description: 'Berisi design uiux, dan user flow dari aplikasi internify',
|
||||
},
|
||||
]
|
||||
import type { TaskTimelineProps } from "../../../../lib/mockDeadline"
|
||||
import { defaultTasks } from "../../../../lib/mockDeadline"
|
||||
|
||||
const TaskTimeline = ({ tasks = defaultTasks }: TaskTimelineProps) => {
|
||||
const today = new Date()
|
||||
const today = new Date()
|
||||
|
||||
const isOverdue = (tanggal: string) => {
|
||||
const deadline = new Date(tanggal)
|
||||
return deadline < 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>
|
||||
@@ -45,6 +23,7 @@ const TaskTimeline = ({ tasks = defaultTasks }: TaskTimelineProps) => {
|
||||
<ul className="flex flex-col gap-5">
|
||||
{tasks.map((task, index) => {
|
||||
const overdue = isOverdue(task.tanggal)
|
||||
const isRedDot = overdue || index === upcomingIndex
|
||||
|
||||
return (
|
||||
<li key={index} className="flex gap-4 pl-0">
|
||||
@@ -52,9 +31,9 @@ const TaskTimeline = ({ tasks = defaultTasks }: TaskTimelineProps) => {
|
||||
<div className="relative flex-shrink-0 mt-1">
|
||||
<span
|
||||
className={`block w-3 h-3 rounded-full border-2 z-10 relative
|
||||
${overdue
|
||||
? 'bg-red-500 border-red-500'
|
||||
: 'bg-white border-gray-300'
|
||||
${isRedDot
|
||||
? 'bg-red-800 '
|
||||
: 'bg-gray-400'
|
||||
}`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user