fixed internship list and internship detail admin
This commit is contained in:
@@ -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 [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];
|
||||
|
||||
useEffect(() => {
|
||||
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"
|
||||
|
||||
@@ -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"
|
||||
>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user