feat: enhance project and mentor management features

- Updated MentorProjectCard to provide more detailed success/error messages upon archiving and completing projects.
- Adjusted layout for certificate generation to improve visual alignment.
- Introduced resolveErrorMessage utility to standardize error message handling across the application.
- Enhanced customToast functionality to handle hook failures more gracefully.
- Refactored useCertificates hook to return structured responses for certificate claims and generation.
- Updated useMentors hook to improve error handling and response structure for mentor operations.
- Improved useNotification hook to return structured responses for marking notifications as read.
- Refactored useProjects hook to provide structured responses for project creation, updates, and member assignments.
- Enhanced useTasks hook to return structured responses for task creation, updates, and deletions.
- Updated services to return structured responses with messages for various API calls related to certificates, mentors, notifications, projects, tasks, and user management.
- Moved mentor-related types to a dedicated user.types file for better organization.
This commit is contained in:
Kemas M. Aryadary Rasyad
2026-07-16 22:50:56 +07:00
parent 99082efbdb
commit 62aaf2bcba
34 changed files with 408 additions and 425 deletions
@@ -62,9 +62,8 @@ export default function CertificateAvailable({ project, certificate, templateUrl
setDownloadingImg(true);
try {
await downloadCertificateImage(certificate, templateUrl, "png");
customToast.success("Sertifikat berhasil diunduh!");
} catch {
customToast.error("Gagal mengunduh sertifikat!");
customToast.error("Gagal mengunduh sertifikat!", "Terjadi kendala saat mengunduh sertifikat!");
} finally {
setDownloadingImg(false);
}
@@ -74,9 +73,8 @@ export default function CertificateAvailable({ project, certificate, templateUrl
setDownloadingPdf(true);
try {
await downloadCertificatePdf(certificate, templateUrl);
customToast.success("Sertifikat berhasil diunduh!");
} catch {
customToast.error("Gagal mengunduh sertifikat!");
customToast.error("Gagal mengunduh sertifikat!", "Terjadi kendala saat mengunduh sertifikat!");
} finally {
setDownloadingPdf(false);
}
@@ -54,7 +54,7 @@ export default function TaskFormFile({ taskId, projectId, deadline, initialSubmi
if (files.length === 0) {
customToast.error(
"File belum dipilih!",
"Silakan pilih file terlebih dahulu sebelum mengirim."
"Choose your file before submit it!"
);
return;
}
@@ -63,11 +63,12 @@ export default function TaskFormFile({ taskId, projectId, deadline, initialSubmi
isEditing ? updateFile(submission!.id, files) : submitFile(files),
{
loading: isEditing ? "Memperbarui file..." : "Mengirim file...",
success: () => ({
success: (res) => ({
title: isEditing ? "File berhasil diperbarui!" : "File berhasil dikirim!",
description: isEditing
? "Perubahan submission Anda telah disimpan."
: "Submission Anda telah berhasil dikirim.",
description: res?.message ??
(isEditing
? "Perubahan submission Anda telah disimpan."
: "Link berhasil dikirim."),
}),
error: (err) => ({
title: isEditing ? "Gagal memperbarui file!" : "Gagal mengirim file!",
@@ -86,8 +87,6 @@ export default function TaskFormFile({ taskId, projectId, deadline, initialSubmi
};
const handleDelete = async () => {
if (!confirm("Yakin ingin menghapus submission ini?")) return;
try {
const ok = await remove(submission!.id);
if (ok) {
@@ -95,7 +94,7 @@ export default function TaskFormFile({ taskId, projectId, deadline, initialSubmi
setIsEditing(false);
customToast.success(
"Submission berhasil dihapus!",
"Anda dapat mengirim submission baru kapan saja."
ok.message
);
}
} catch (err: any) {
@@ -27,24 +27,18 @@ export default function TaskFormLink({
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!inputLink.trim()) {
customToast.error(
"Link belum dibuat",
"Masukkan link untuk mengirim submission"
);
return;
}
try {
await customToast.promise(
isEditing ? updateLink(submission!.id, inputLink) : submitLink(inputLink),
{
loading: isEditing ? "Memperbarui Link..." : "Mengirim Link...",
success: () => ({
success: (res) => ({
title: isEditing ? "Link diperbaharui" : "Link berhasil dikirim",
description: isEditing
? "Perubahan submission Anda telah disimpan."
: "Submission Anda telah berhasil dikirim.",
description:
res?.message ??
(isEditing
? "Perubahan submission Anda telah disimpan."
: "Link berhasil dikirim."),
}),
error: (err) => ({
title: isEditing ? "Gagal memperbarui link!" : "Gagal mengirim link!",
@@ -56,13 +50,10 @@ export default function TaskFormLink({
}
);
setIsEditing(false);
} catch {
// error sudah ditangani toast, tetap di form
}
} catch { }
};
const handleDelete = async () => {
if (!confirm("Yakin ingin menghapus submission ini?")) return;
try {
const ok = await remove(submission!.id);
if (ok) {
@@ -70,7 +61,7 @@ export default function TaskFormLink({
setIsEditing(false);
customToast.success(
"Submission berhasil dihapus!",
"Anda dapat mengirim submission baru kapan saja."
ok.message
);
}
} catch (err: any) {
@@ -90,7 +81,7 @@ export default function TaskFormLink({
deadline={deadline}
onEdit={() => setIsEditing(true)}
onDelete={handleDelete}
disabled={!isProjectActive}
disabled={!isProjectActive}
/>
);
}