add navigation on internify header: navigation to home from humic logo +
crosscheck line code
This commit is contained in:
@@ -5,38 +5,39 @@ import logoHumic from "../assets/logo.png";
|
|||||||
const internshipLinks = [
|
const internshipLinks = [
|
||||||
{ label: "Beranda", path: "/" },
|
{ label: "Beranda", path: "/" },
|
||||||
{ label: "Tentang", path: "/about-us" },
|
{ label: "Tentang", path: "/about-us" },
|
||||||
{ label: "Life At Humic", path: "/our-developer" },
|
{ label: "Life At Humic", path: "" },
|
||||||
|
{ label: "Our Developer", path: "/our-developer" }
|
||||||
];
|
];
|
||||||
|
|
||||||
const externalLinks = [
|
const externalLinks = [
|
||||||
{ label: "Humic Website", href: "https://humic.telkomuniversity.ac.id/" },
|
{ label: "Humic Website", href: "https://humic.telkomuniversity.ac.id/" },
|
||||||
{ label: "Our Activity", href: "https://humic.telkomuniversity.ac.id/humicinmedia/" },
|
{ label: "Our Activity", href: "https://humic.telkomuniversity.ac.id/humicinmedia/" },
|
||||||
{ label: "Life At Humic", href: "#" },
|
{ label: "Life At Humic", href: "" },
|
||||||
{ label: "Our Developer", href: "#" },
|
// { label: "Our Developer", href: "#" },
|
||||||
];
|
];
|
||||||
|
|
||||||
const socials = [
|
const socials = [
|
||||||
{ label: "Instagram", icon: FaInstagram, href: "https://www.instagram.com/humicengineering/" },
|
{ label: "Instagram", icon: FaInstagram, href: "https://www.instagram.com/humicengineering/" },
|
||||||
{ label: "LinkedIn", icon: FaLinkedin, href: "https://www.linkedin.com/company/humic-engineering/" },
|
{ label: "LinkedIn", icon: FaLinkedin, href: "https://www.linkedin.com/company/humic-engineering/" },
|
||||||
{ label: "Email", icon: FaRegEnvelope, href: "#" },
|
{ label: "Email", icon: FaRegEnvelope, href: "" },
|
||||||
];
|
];
|
||||||
|
|
||||||
export function FooterIntern() {
|
export function FooterIntern() {
|
||||||
return (
|
return (
|
||||||
<footer className="bg-white border-t border-gray-300 px-14 py-8 flex gap-12 flex-wrap">
|
<footer className="bg-white border-t border-gray-300 px-14 py-8 flex gap-12 flex-wrap">
|
||||||
{/* Brand info */}
|
{/* Brand info */}
|
||||||
<div className="flex flex-col gap-2 flex-shrink-0 w-70 items-start">
|
<div className="flex flex-col gap-1 flex-shrink-0 w-70 items-start">
|
||||||
<img
|
<img
|
||||||
src={logoHumic}
|
src={logoHumic}
|
||||||
alt="logo humic"
|
alt="logo humic"
|
||||||
className="w-[160px]"
|
className="w-[130px]"
|
||||||
/>
|
/>
|
||||||
<address className="text-xs text-gray-800 leading-relaxed not-italic">
|
<address className="text-[13px] text-gray-800 not-italic mt-[10px]">
|
||||||
Gedung Kultubai Selatan, Blok F
|
Gedung Kultubai Selatan, Blok F
|
||||||
Jl. Telekomunikasi, Terusan Buah Batu Bandung
|
Jl. Telekomunikasi, Terusan Buah Batu Bandung
|
||||||
Jawa Barat, Indonesia. 40257
|
Jawa Barat, Indonesia. 40257
|
||||||
</address>
|
</address>
|
||||||
<div className="flex gap-2 mt-1">
|
<div className="flex gap-2 mt-[13px]">
|
||||||
{socials.map(({ label, icon: Icon, href }) => (
|
{socials.map(({ label, icon: Icon, href }) => (
|
||||||
<a
|
<a
|
||||||
key={label}
|
key={label}
|
||||||
@@ -56,7 +57,7 @@ export function FooterIntern() {
|
|||||||
<div className="flex gap-12 flex-1 justify-end flex-wrap">
|
<div className="flex gap-12 flex-1 justify-end flex-wrap">
|
||||||
<div className="flex flex-col gap-2 min-w-[140px]">
|
<div className="flex flex-col gap-2 min-w-[140px]">
|
||||||
<p className="text-[13px] font-bold text-gray-900">Internship</p>
|
<p className="text-[13px] font-bold text-gray-900">Internship</p>
|
||||||
<ul className="flex flex-col gap-0.5">
|
<ul className="flex flex-col gap-0.5 font-semibold">
|
||||||
{internshipLinks.map(({ label, path }) => (
|
{internshipLinks.map(({ label, path }) => (
|
||||||
<li key={label}>
|
<li key={label}>
|
||||||
<Link
|
<Link
|
||||||
@@ -71,8 +72,8 @@ export function FooterIntern() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col gap-2 min-w-[140px]">
|
<div className="flex flex-col gap-2 min-w-[140px]">
|
||||||
<p className="text-[13px] font-bold text-gray-900">Eksternal</p>
|
<p className="text-[13px] font-bold text-gray-900">Lainnya</p>
|
||||||
<ul className="flex flex-col gap-0.5">
|
<ul className="flex flex-col gap-0.5 font-semibold">
|
||||||
{externalLinks.map(({ label, href }) => (
|
{externalLinks.map(({ label, href }) => (
|
||||||
<li key={label}>
|
<li key={label}>
|
||||||
<a
|
<a
|
||||||
|
|||||||
@@ -32,7 +32,9 @@ export default function Header({ toggleSidebar }: { toggleSidebar: () => void })
|
|||||||
<Menu className="w-6 h-6" />
|
<Menu className="w-6 h-6" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button>
|
<button
|
||||||
|
onClick={() => nav("")}
|
||||||
|
>
|
||||||
<img src={humiclogo} alt="Humic Logo" className="w-[120px] cursor-pointer" />
|
<img src={humiclogo} alt="Humic Logo" className="w-[120px] cursor-pointer" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const Navbar = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav className="bg-[#D7E1E8] px-4 py-5 mx-5 rounded-2xl shadow-md">
|
<nav className="bg-[#D7E1E8] px-10 py-5 mx-5 rounded-2xl shadow-md">
|
||||||
<div className="max-w-8xl mx-auto flex items-center justify-between">
|
<div className="max-w-8xl mx-auto flex items-center justify-between">
|
||||||
<img
|
<img
|
||||||
src={humiclogo}
|
src={humiclogo}
|
||||||
@@ -56,22 +56,22 @@ const Navbar = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Desktop menu */}
|
{/* Desktop menu */}
|
||||||
<div className="hidden md:flex items-center gap-6 text-[18px] font-semibold">
|
<div className="hidden md:flex items-center gap-8 text-[18px] font-semibold">
|
||||||
<button
|
<button
|
||||||
onClick={() => navigate("/about-us")}
|
onClick={() => navigate("/about-us")}
|
||||||
className="hover:text-blue-600 transition cursor-pointer"
|
className="hover:text-gray-600 transition cursor-pointer"
|
||||||
>
|
>
|
||||||
About Us
|
About Us
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={handleContactClick}
|
onClick={handleContactClick}
|
||||||
className="hover:text-blue-600 transition cursor-pointer"
|
className="hover:text-gray-600 transition cursor-pointer"
|
||||||
>
|
>
|
||||||
Contact Us
|
Contact Us
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => navigate("/internships")}
|
onClick={() => navigate("/internships")}
|
||||||
className="hover:text-blue-600 transition cursor-pointer"
|
className="hover:text-gray-600 transition cursor-pointer"
|
||||||
>
|
>
|
||||||
Internship
|
Internship
|
||||||
</button>
|
</button>
|
||||||
@@ -80,7 +80,7 @@ const Navbar = () => {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
navigate("/login-internify");
|
navigate("/login-internify");
|
||||||
}}
|
}}
|
||||||
className="block w-full text-left hover:text-red-600 transition"
|
className="hover:text-red-600 transition cursor-pointer"
|
||||||
>
|
>
|
||||||
Portal LMS
|
Portal LMS
|
||||||
</button>
|
</button>
|
||||||
@@ -125,7 +125,7 @@ const Navbar = () => {
|
|||||||
}}
|
}}
|
||||||
className="block w-full text-left hover:text-red-600 transition"
|
className="block w-full text-left hover:text-red-600 transition"
|
||||||
>
|
>
|
||||||
Login
|
Portal LMS
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// src/components/common/Avatar.tsx
|
|
||||||
|
|
||||||
export default function Avatar({ initials }: { initials: string }) {
|
export default function Avatar({ initials }: { initials: string }) {
|
||||||
return (
|
return (
|
||||||
<div className="w-10 h-10 rounded-full flex items-center justify-center text-xs text-white flex-shrink-0 bg-primary">
|
<div className="w-10 h-10 rounded-full flex items-center justify-center text-xs text-white flex-shrink-0 bg-primary">
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ const TaskBreadcrumb = ({ match }: BreadcrumbMatch) => {
|
|||||||
return <>{task?.title ?? unslugify(taskSlug)}</>;
|
return <>{task?.title ?? unslugify(taskSlug)}</>;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Breadcrumb untuk halaman detail sertifikat (menggunakan nama project dari slug)
|
|
||||||
const CertBreadcrumb = ({ match }: BreadcrumbMatch) => {
|
const CertBreadcrumb = ({ match }: BreadcrumbMatch) => {
|
||||||
const { projects } = useProjects();
|
const { projects } = useProjects();
|
||||||
const slug = match.params.slug;
|
const slug = match.params.slug;
|
||||||
@@ -37,7 +36,6 @@ const CertBreadcrumb = ({ match }: BreadcrumbMatch) => {
|
|||||||
return <>{project?.project_name ?? unslugify(slug)}</>;
|
return <>{project?.project_name ?? unslugify(slug)}</>;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Breadcrumb untuk halaman submission intern (unslugify nameIntern)
|
|
||||||
const NameInternBreadcrumb = ({ match }: BreadcrumbMatch) => {
|
const NameInternBreadcrumb = ({ match }: BreadcrumbMatch) => {
|
||||||
return <>{unslugify(match.params.nameIntern)}</>;
|
return <>{unslugify(match.params.nameIntern)}</>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ interface FAQ {
|
|||||||
created_at: string;
|
created_at: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skeleton item that mirrors the real FAQ card shape
|
|
||||||
function FAQSkeleton() {
|
function FAQSkeleton() {
|
||||||
return (
|
return (
|
||||||
<div className="relative shadow-md py-5 px-10 rounded-md bg-white border-l-6 border-gray-200 animate-pulse">
|
<div className="relative shadow-md py-5 px-10 rounded-md bg-white border-l-6 border-gray-200 animate-pulse">
|
||||||
|
|||||||
@@ -58,9 +58,9 @@ export default function InternifyLogin() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background flex flex-col justify-between items-center py-12 px-4 font-sans selection:bg-red-foreground">
|
<div className="min-h-screen bg-background flex flex-col justify-between items-center py-6 px-4 selection:bg-red-foreground">
|
||||||
<div className="w-full flex flex-col items-center">
|
<div className="w-full flex flex-col items-center mb-8">
|
||||||
<div className="text-center mb-12">
|
<div className="text-center mb-8">
|
||||||
<div className="flex items-center justify-center gap-2 font-bold text-2xl tracking-tight text-black">
|
<div className="flex items-center justify-center gap-2 font-bold text-2xl tracking-tight text-black">
|
||||||
<img src={logo} alt="Logo" />
|
<img src={logo} alt="Logo" />
|
||||||
</div>
|
</div>
|
||||||
@@ -129,9 +129,9 @@ export default function InternifyLogin() {
|
|||||||
</form>
|
</form>
|
||||||
{/* Forgot Password */}
|
{/* Forgot Password */}
|
||||||
<div className="text-center mt-6">
|
<div className="text-center mt-6">
|
||||||
<a href="#" className="text-xs font-semibold text-red-600 hover:underline">
|
<span className="text-xs font-semibold text-red-600">
|
||||||
Forgot Password?
|
Forgot Password?
|
||||||
</a>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Divider & Support */}
|
{/* Divider & Support */}
|
||||||
@@ -144,7 +144,7 @@ export default function InternifyLogin() {
|
|||||||
<div className="flex-grow border-t border-card-outline"></div>
|
<div className="flex-grow border-t border-card-outline"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button className="w-full mt-2 flex items-center justify-center gap-2 border border-card-outline rounded-full py-1.5 text-xs text-font-shade hover:bg-gray-50 transition-colors font-medium">
|
<button className="w-full mt-2 flex items-center justify-center gap-2 border border-card-outline rounded-full py-1.5 text-xs text-font-shade font-medium">
|
||||||
<CircleQuestionMark className="w-5 h-5" />
|
<CircleQuestionMark className="w-5 h-5" />
|
||||||
Frequently Asked Questions
|
Frequently Asked Questions
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export default function ProjectsPage() {
|
|||||||
<h1 className="page-title-desc">View assigned projects and track their progress.</h1>
|
<h1 className="page-title-desc">View assigned projects and track their progress.</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-wrap gap-4 items-stretch">
|
<div className="flex flex-wrap gap-3 items-stretch">
|
||||||
{renderContent()}
|
{renderContent()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ export default function MentorProjectsPage() {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-wrap gap-4 items-stretch">{renderContent()}</div>
|
<div className="flex flex-wrap gap-3 items-stretch">{renderContent()}</div>
|
||||||
|
|
||||||
{isModalOpen && (
|
{isModalOpen && (
|
||||||
<CreateProjectModal
|
<CreateProjectModal
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ export default function CertificateDetail() {
|
|||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* ── Project Scope ── */}
|
{/* Project Scope */}
|
||||||
<div className="border border-card-outline rounded-xl p-5">
|
<div className="border border-card-outline rounded-xl p-5">
|
||||||
<p className="text-[13px] font-bold text-gray-400 uppercase tracking-widest mb-3">
|
<p className="text-[13px] font-bold text-gray-400 uppercase tracking-widest mb-3">
|
||||||
Project Scope
|
Project Scope
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export default function CertificateList() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Loading Skeleton — struktur sama persis dengan body */}
|
{/* Loading Skeleton - struktur sama persis dengan body */}
|
||||||
{loading && (
|
{loading && (
|
||||||
<div>
|
<div>
|
||||||
<div className="h-4 w-32 bg-gray-200 rounded mb-4 animate-pulse" />
|
<div className="h-4 w-32 bg-gray-200 rounded mb-4 animate-pulse" />
|
||||||
@@ -73,7 +73,7 @@ export default function CertificateList() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Section: List Projects — hanya tampil kalau ada project */}
|
{/* Section: List Projects - hanya tampil kalau ada project */}
|
||||||
{hasProjects && (
|
{hasProjects && (
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-base font-bold text-font-shade mb-4">List Projects</h2>
|
<h2 className="text-base font-bold text-font-shade mb-4">List Projects</h2>
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import { FileText } from 'lucide-react';
|
|||||||
export default function MentorForumTab({ project }: { project: Project }) {
|
export default function MentorForumTab({ project }: { project: Project }) {
|
||||||
const { tasks, loading, error } = useProjectTasks(project.id);
|
const { tasks, loading, error } = useProjectTasks(project.id);
|
||||||
|
|
||||||
// Skeleton meniru layout final: kolom timeline 1/5 + list task card 4/5
|
|
||||||
const renderSkeleton = () => (
|
const renderSkeleton = () => (
|
||||||
<div className="flex flex-col gap-7 md:flex-row md:items-start mt-5 animate-pulse">
|
<div className="flex flex-col gap-7 md:flex-row md:items-start mt-5 animate-pulse">
|
||||||
<div className="w-full md:w-1/5 bg-white rounded-xl p-5 border border-box-border shrink-0">
|
<div className="w-full md:w-1/5 bg-white rounded-xl p-5 border border-box-border shrink-0">
|
||||||
@@ -29,14 +28,12 @@ export default function MentorForumTab({ project }: { project: Project }) {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
// Empty hanya valid setelah loading selesai & tidak error
|
|
||||||
const isEmpty = !loading && !error && tasks.length === 0;
|
const isEmpty = !loading && !error && tasks.length === 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<HeroProject title={project.project_name} description={project.description} />
|
<HeroProject title={project.project_name} description={project.description} />
|
||||||
|
|
||||||
{/* Kondisi eksklusif: hanya satu state tampil dalam satu waktu */}
|
|
||||||
{loading ? (
|
{loading ? (
|
||||||
renderSkeleton()
|
renderSkeleton()
|
||||||
) : error ? (
|
) : error ? (
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// utils/toast.tsx
|
|
||||||
import toast, { type Toast } from 'react-hot-toast';
|
import toast, { type Toast } from 'react-hot-toast';
|
||||||
import { FiX } from 'react-icons/fi';
|
import { FiX } from 'react-icons/fi';
|
||||||
import { FaCheck } from "react-icons/fa";
|
import { FaCheck } from "react-icons/fa";
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// hooks/useCertificates.ts
|
|
||||||
import { useState, useEffect, useCallback } from "react";
|
import { useState, useEffect, useCallback } from "react";
|
||||||
import type { Certificate, CertificateVerifyResult } from "../types/certificate.types";
|
import type { Certificate, CertificateVerifyResult } from "../types/certificate.types";
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// hooks/useNotifications.ts
|
|
||||||
import { useState, useEffect, useCallback } from "react";
|
import { useState, useEffect, useCallback } from "react";
|
||||||
import {
|
import {
|
||||||
getNotifications,
|
getNotifications,
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// hooks/useProject.ts
|
|
||||||
import { useState, useEffect, useCallback } from "react";
|
import { useState, useEffect, useCallback } from "react";
|
||||||
import type {
|
import type {
|
||||||
Project,
|
Project,
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// hooks/useTasks.ts
|
|
||||||
import { useState, useEffect, useCallback, useMemo } from "react";
|
import { useState, useEffect, useCallback, useMemo } from "react";
|
||||||
import type {
|
import type {
|
||||||
ProjectTask,
|
ProjectTask,
|
||||||
@@ -46,7 +45,7 @@ export const useProjectTasks = (projectId: number) => {
|
|||||||
refetch();
|
refetch();
|
||||||
}, [refetch]);
|
}, [refetch]);
|
||||||
|
|
||||||
return { tasks, loading, error, refetch }; // tambah refetch
|
return { tasks, loading, error, refetch };
|
||||||
};
|
};
|
||||||
|
|
||||||
// GET /task-api/tasks/{id}
|
// GET /task-api/tasks/{id}
|
||||||
@@ -276,7 +275,7 @@ export const useAllMentorTasks = (enabled: boolean) => {
|
|||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!enabled) return; // tidak fetch kalau intern
|
if (!enabled) return;
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
getAllMentorTasks()
|
getAllMentorTasks()
|
||||||
.then(setTasks)
|
.then(setTasks)
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// hooks/useUser.ts
|
import { useState, useEffect, useCallback, useSyncExternalStore } from "react";
|
||||||
import { useState, useEffect, useCallback, useRef, useSyncExternalStore } from "react";
|
|
||||||
import { loginUser, getProfile, updateProfile } from "../services/UserService";
|
import { loginUser, getProfile, updateProfile } from "../services/UserService";
|
||||||
import type { Mahasiswa } from "../types/project.types";
|
import type { Mahasiswa } from "../types/project.types";
|
||||||
import { getMahasiswa } from "../services/MahasiswaService";
|
import { getMahasiswa } from "../services/MahasiswaService";
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// services/mahasiswa.service.ts
|
|
||||||
import api from '../lib/api';
|
import api from '../lib/api';
|
||||||
import type { Mahasiswa, MahasiswaResponse } from "../types/project.types"
|
import type { Mahasiswa, MahasiswaResponse } from "../types/project.types"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// services/ProjectService.ts
|
|
||||||
import api from "../lib/api";
|
import api from "../lib/api";
|
||||||
import type {
|
import type {
|
||||||
Project,
|
Project,
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// services/TaskService.ts
|
|
||||||
import api from "../lib/api";
|
import api from "../lib/api";
|
||||||
import type {
|
import type {
|
||||||
ProjectTask,
|
ProjectTask,
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// services/UserService.ts
|
|
||||||
import api from "../lib/api";
|
import api from "../lib/api";
|
||||||
import type { CurrentUser, UpdateProfilePayload } from "../types/user.types";
|
import type { CurrentUser, UpdateProfilePayload } from "../types/user.types";
|
||||||
import type { Mahasiswa, MahasiswaResponse } from "../types/project.types";
|
import type { Mahasiswa, MahasiswaResponse } from "../types/project.types";
|
||||||
|
|||||||
Reference in New Issue
Block a user