fixed internship list and internship detail admin

This commit is contained in:
Billy Marcello
2025-11-28 00:00:49 +07:00
parent 96ee371975
commit 7966c0bb0d
3 changed files with 120 additions and 21 deletions
+94 -10
View File
@@ -4,16 +4,35 @@ import NavbarAdmin from "../Layout/NavbarAdmin";
import SidebarAdmin from "../Layout/SidebarAdmin";
import axios from "axios";
type Applicant = {
id: number;
nama_depan: string;
nama_belakang: string;
email: string;
kontak: string;
jurusan: string;
motivasi: string;
relevant_skills: string;
cv_path: string;
portofolio_path: string;
posisi: string;
kelompok_peminatan?: string;
status: string;
};
const InternshipDetailsAdmin = () => {
const { id } = useParams(); // id lamaran magang
const [applicant, setApplicant] = useState<any>(null);
const token = document.cookie
.split("; ")
.find((row) => row.startsWith("token="))
?.split("=")[1];
const [applicant, setApplicant] = useState<Applicant | null>(null);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
if (!id) return;
const token = document.cookie
.split("; ")
.find((row) => row.startsWith("token="))
?.split("=")[1];
axios
.get(
`${import.meta.env.VITE_API_BASE_URL}/lamaran-magang-api/get/${id}`,
@@ -24,14 +43,74 @@ const InternshipDetailsAdmin = () => {
}
)
.then((res) => {
setApplicant(res.data.data[0]);
console.log("Detail lamaran:", res.data);
const raw =
Array.isArray(res.data?.data) && res.data.data.length > 0
? res.data.data[0]
: Array.isArray(res.data)
? res.data[0]
: res.data?.data ?? res.data;
if (!raw) {
setError("Data lamaran tidak ditemukan.");
return;
}
console.log("Raw detail:", raw); // cek struktur asli
const mapped: Applicant = {
id: raw.id,
// dari relasi mahasiswa
nama_depan: raw.mahasiswa?.nama_depan ?? "",
nama_belakang: raw.mahasiswa?.nama_belakang ?? "",
email: raw.mahasiswa?.email ?? raw.email ?? "",
kontak: raw.mahasiswa?.kontak ?? raw.kontak ?? "",
jurusan: raw.mahasiswa?.jurusan ?? raw.jurusan ?? "",
// dari relasi lowongan_magang
posisi: raw.lowongan_magang?.posisi ?? raw.posisi ?? "",
kelompok_peminatan:
raw.lowongan_magang?.kelompok_peminatan ??
raw.kelompok_peminatan ??
"",
// 🔥 ini dia: ambil dari mahasiswa
motivasi: raw.mahasiswa?.motivasi ?? raw.motivasi ?? "",
relevant_skills:
raw.mahasiswa?.relevant_skills ??
raw.relevant_skills ??
raw.relevant_skill ??
"",
// sekalian amankan CV & portofolio kalau memang ikut di object mahasiswa
cv_path: raw.cv_path ?? raw.mahasiswa?.cv_path ?? "",
portofolio_path:
raw.portofolio_path ?? raw.mahasiswa?.portofolio_path ?? "",
status: raw.status ?? "diproses",
};
setApplicant(mapped);
setError(null);
})
.catch((err) => {
console.error("Gagal fetch detail lamaran", err);
setError("Gagal mengambil detail lamaran.");
});
}, [id]);
const token = document.cookie
.split("; ")
.find((row) => row.startsWith("token="))
?.split("=")[1];
const handleStatusUpdate = (status: "diterima" | "ditolak") => {
if (!id) return;
axios
.patch(
`${import.meta.env.VITE_API_BASE_URL}/lamaran-magang-api/update/${id}`,
@@ -44,13 +123,17 @@ const InternshipDetailsAdmin = () => {
)
.then(() => {
alert(`Status berhasil diubah menjadi ${status}`);
setApplicant((prev: any) => ({ ...prev, status }));
setApplicant((prev) => (prev ? { ...prev, status } : prev));
})
.catch((err) => {
console.error("Gagal update status", err);
});
};
if (error) {
return <p className="p-10 text-red-500">{error}</p>;
}
if (!applicant) return <p className="p-10">Loading...</p>;
return (
@@ -117,9 +200,10 @@ const InternshipDetailsAdmin = () => {
</p>
<div className="flex gap-2 items-center">
<h2 className="font-semibold">Skill:</h2>
{applicant.relevant_skills
{(applicant.relevant_skills || "")
.split(",")
.map((skill: string, idx: number) => (
.filter((s) => s.trim() !== "")
.map((skill, idx) => (
<span
key={idx}
className="text-[#9F9F9F] font-semibold px-3 py-1 border border-[#9F9F9F] rounded-3xl"
+21 -11
View File
@@ -32,7 +32,16 @@ const InternshipList = () => {
},
})
.then((res) => {
setApplications(res.data.data || []);
const raw = Array.isArray(res.data.data) ? res.data.data : [];
const mapped: Application[] = raw.map((item: any) => ({
id: item.id,
nama_depan: item.mahasiswa?.nama_depan ?? "",
posisi: item.lowongan_magang?.posisi ?? "",
status: item.status ?? "diproses",
}));
setApplications(mapped);
})
.catch((err) => {
console.error("Gagal mengambil data lamaran:", err);
@@ -42,7 +51,6 @@ const InternshipList = () => {
fetchApplications();
const interval = setInterval(fetchApplications, 10000);
return () => clearInterval(interval);
}, []);
@@ -62,11 +70,13 @@ const InternshipList = () => {
const indexOfLastItem = currentPage * itemsPerPage;
const indexOfFirstItem = indexOfLastItem - itemsPerPage;
const filteredApplications = applications.filter(
(app) =>
app.nama_depan.toLowerCase().includes(searchTerm.toLowerCase()) ||
app.posisi.toLowerCase().includes(searchTerm.toLowerCase())
);
const filteredApplications = applications.filter((app) => {
const term = searchTerm.toLowerCase();
const nama = (app.nama_depan ?? "").toLowerCase();
const posisi = (app.posisi ?? "").toLowerCase();
return nama.includes(term) || posisi.includes(term);
});
const currentItems = filteredApplications.slice(
indexOfFirstItem,
@@ -213,17 +223,17 @@ const InternshipList = () => {
<td className="px-6 py-3">
<span
className={`px-3 py-1 rounded-full font-semibold text-sm ${getStatusStyle(
app.status
app.status ?? "diproses"
)}`}
>
{app.status.charAt(0).toUpperCase() +
app.status.slice(1)}
{(app.status ?? "diproses").charAt(0).toUpperCase() +
(app.status ?? "diproses").slice(1)}
</span>
</td>
<td className="px-6 py-3">
<button
onClick={() =>
navigate(`/InternshipsDetailsAdmin/${app.id}`)
navigate(`/internships-details-admin/${app.id}`)
}
className="bg-blue-500 hover:bg-blue-600 cursor-pointer text-white px-4 py-2 rounded-lg"
>
+5
View File
@@ -183,6 +183,11 @@ const PartnershipsList = () => {
})}
</tbody>
</table>
{partnerships.length === 0 && (
<p className="text-gray-500 mt-4 text-center">Belum ada data partnerships.</p>
)}
</div>
</div>
</div>