feat: implement authentication system, role-based routing, and profile settings for interns and mentors
This commit is contained in:
@@ -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 };
|
||||
};
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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 };
|
||||
};
|
||||
Reference in New Issue
Block a user