feat: implement authentication system, role-based routing, and profile settings for interns and mentors

This commit is contained in:
Kemas M. Aryadary Rasyad
2026-06-28 10:13:50 +07:00
parent ad26a53f21
commit 37e21a1f39
17 changed files with 369 additions and 226 deletions
+6 -3
View File
@@ -1,5 +1,5 @@
// hooks/useMahasiswa.ts
import { useState, useEffect } from 'react';
import { useState, useEffect, useCallback } from 'react';
import type { Mahasiswa } from '../types/project.types';
import { getMahasiswa } from '../services/MahasiswaService';
@@ -8,12 +8,15 @@ export const useMahasiswa = () => {
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const refetch = useCallback(() => {
setLoading(true);
getMahasiswa()
.then(setMahasiswa)
.catch(() => setError('Gagal memuat data mahasiswa.'))
.finally(() => setLoading(false));
}, []);
return { mahasiswa, loading, error };
useEffect(() => { refetch(); }, [refetch]);
return { mahasiswa, loading, error, refetch };
};
+16 -8
View File
@@ -67,7 +67,7 @@ export const useProjectDetail = (id: string) => {
//Khusus Ambil Intern dari Project/{id}
export const useProjectInterns = (projectId: string) => {
const [interns, setInterns] = useState<ProjectMember[]>([] );
const [interns, setInterns] = useState<ProjectMember[]>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
@@ -89,30 +89,35 @@ export const useMyProjects = () => {
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const refetch = useCallback(() => {
setLoading(true);
getMyProjects()
.then(setProjects)
.catch(() => setError("Gagal memuat my projects."))
.finally(() => setLoading(false));
}, []);
return { projects, loading, error };
};
useEffect(() => { refetch(); }, [refetch]);
return { projects, loading, error, refetch };
};
// GET /project-api/my-tasks
export const useMyTasks = () => {
const [tasks, setTasks] = useState<InternTaskItem[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const refetch = useCallback(() => {
setLoading(true);
getMyTasks()
.then(setTasks)
.catch(() => setError("Gagal memuat tasks."))
.finally(() => setLoading(false));
}, []);
return { tasks, loading, error };
useEffect(() => { refetch(); }, [refetch]);
return { tasks, loading, error, refetch };
};
// GET /project-api/mentor-projects
@@ -121,14 +126,17 @@ export const useMentorProjects = () => {
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const refetch = useCallback(() => {
setLoading(true);
getMentorProjects()
.then(setProjects)
.catch(() => setError("Gagal memuat mentor projects."))
.finally(() => setLoading(false));
}, []);
return { projects, loading, error };
useEffect(() => { refetch(); }, [refetch]);
return { projects, loading, error, refetch };
};
// GET /project-api/interns
+15 -7
View File
@@ -18,30 +18,35 @@ export const useMyCertificates = () => {
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const refetch = useCallback(() => {
setLoading(true);
getMyCertificates()
.then(setCertificates)
.catch(() => setError("Gagal memuat sertifikat."))
.finally(() => setLoading(false));
}, []);
return { certificates, loading, error };
};
useEffect(() => { refetch(); }, [refetch]);
return { certificates, loading, error, refetch };
};
// GET /certificate-api/all
export const useAllCertificates = () => {
const [certificates, setCertificates] = useState<Certificate[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const refetch = useCallback(() => {
setLoading(true);
getAllCertificates()
.then(setCertificates)
.catch(() => setError("Gagal memuat semua sertifikat."))
.finally(() => setLoading(false));
}, []);
return { certificates, loading, error };
useEffect(() => { refetch(); }, [refetch]);
return { certificates, loading, error, refetch };
};
// GET /certificate-api/detail/{id}
@@ -67,15 +72,18 @@ export const useProjectCertificates = (id_project: number) => {
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const refetch = useCallback(() => {
if (!id_project) return;
setLoading(true);
getCertificatesByProject(id_project)
.then(setCertificates)
.catch(() => setError("Gagal memuat sertifikat project."))
.finally(() => setLoading(false));
}, [id_project]);
return { certificates, loading, error };
useEffect(() => { refetch(); }, [refetch]);
return { certificates, loading, error, refetch };
};
// GET /certificate-api/verify & verify-uuid (public, on-demand)
+81 -7
View File
@@ -1,24 +1,98 @@
import { useState, useEffect } from "react";
import api from "../lib/api";
// hooks/useUser.ts
import { useState, useEffect, useCallback } from "react";
import { loginUser, getProfile, updateProfile } from "../services/UserService";
// ─── Types ────────────────────────────────────────────────────────────────────
export interface CurrentUser {
id: number;
full_name: string;
email: string;
role: "mentor" | "intern" | "admin";
professional_bio?: string;
profile_picture?: string | null;
}
export interface UpdateProfilePayload {
full_name?: string;
email?: string;
professional_bio?: string;
profile_picture?: File | null;
}
// ─── useCurrentUser ───────────────────────────────────────────────────────────
// GET /auth-api/me — ambil data user yang sedang login
export const useCurrentUser = () => {
const [user, setUser] = useState<CurrentUser | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
api.get("/auth-api/me")
.then((res) => setUser(res.data.data))
.catch(() => setError("Gagal memuat user."))
const refetch = useCallback(() => {
setLoading(true);
setError(null);
getProfile()
.then(setUser)
.catch(() => setError("Gagal memuat data user."))
.finally(() => setLoading(false));
}, []);
return { user, loading, error };
useEffect(() => {
refetch();
}, [refetch]);
return { user, loading, error, refetch };
};
// ─── useLogin ────────────────────────────────────────────────────────────────
// POST /auth-api/login
export const useLogin = () => {
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const login = async (email: string, password: string) => {
setLoading(true);
setError(null);
try {
const data = await loginUser({ email, password });
return data; // { token }
} catch (err: any) {
const status = err?.response?.status;
if (status === 401 || status === 400) {
setError("Email atau password salah.");
} else {
setError("Terjadi kesalahan. Coba beberapa saat lagi.");
}
return null;
} finally {
setLoading(false);
}
};
return { login, loading, error, setError };
};
// ─── useUpdateProfile ────────────────────────────────────────────────────────
// PATCH /auth-api/update-profile
export const useUpdateProfile = () => {
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [successMsg, setSuccessMsg] = useState<string | null>(null);
const save = async (payload: UpdateProfilePayload) => {
setLoading(true);
setError(null);
setSuccessMsg(null);
try {
const updated = await updateProfile(payload);
setSuccessMsg("Profile updated successfully!");
return updated;
} catch (err: any) {
setError(err?.response?.data?.message || "Failed to save changes.");
return null;
} finally {
setLoading(false);
}
};
return { save, loading, error, successMsg, setSuccessMsg, setError };
};