feat: enhance task creation and editing with success/error toasts

- Added success and error toast notifications for task creation in CreateTaskDialog.
- Implemented success and error toast notifications for task updates in EditTaskDialog.
- Integrated custom toast notifications in MentorProjectCard for project archiving.
- Removed unused mock data files related to projects, interns, deadlines, and certificates.
- Updated useProjectTasks hook to accept projectId as a number instead of a string.
- Improved error handling in useSubmission hook methods to throw errors instead of returning null.
This commit is contained in:
Kemas M. Aryadary Rasyad
2026-07-06 20:38:30 +07:00
parent 8f5fb82f59
commit 7a331b8e7d
27 changed files with 500 additions and 913 deletions
@@ -4,6 +4,7 @@ import { LuSendHorizontal } from "react-icons/lu";
import SubmitStatusTable from "./SubmissionStatusTable";
import { useSubmission } from "../../../../hooks/useTasks";
import type { TaskSubmissionData } from "../../../../types/task.types";
import { customToast } from "../../../utils/showToast";
interface TaskFormFileProps {
taskId: string;
@@ -25,10 +26,7 @@ export default function TaskFormFile({ taskId, projectId, deadline, initialSubmi
selectedFiles.forEach((file) => {
const ext = file.name.split(".").pop()?.toLowerCase();
if (ext && allowedExtensions.includes(ext)) {
if (file.size <= 10 * 1024 * 1024) validFiles.push(file);
else alert(`Ukuran file ${file.name} melebihi 10MB.`);
} else {
alert(`Format ${file.name} tidak didukung. Gunakan PDF.`);
if (file.size <= 5 * 1024 * 1024) validFiles.push(file);
}
});
if (validFiles.length > 0) setFiles((prev) => [...prev, ...validFiles]);
@@ -52,17 +50,59 @@ export default function TaskFormFile({ taskId, projectId, deadline, initialSubmi
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (files.length === 0) { alert("Pilih file terlebih dahulu."); return; }
const res = isEditing
? await updateFile(submission!.id, files)
: await submitFile(files);
if (res) { setIsEditing(false); setFiles([]); }
if (files.length === 0) {
customToast.error(
"File belum dipilih!",
"Silakan pilih file terlebih dahulu sebelum mengirim."
);
return;
}
const res = await customToast.promise(
isEditing ? updateFile(submission!.id, files) : submitFile(files),
{
loading: isEditing ? "Memperbarui file..." : "Mengirim file...",
success: () => ({
title: isEditing ? "File berhasil diperbarui!" : "File berhasil dikirim!",
description: isEditing
? "Perubahan submission Anda telah disimpan."
: "Submission Anda telah berhasil dikirim.",
}),
error: (err) => ({
title: isEditing ? "Gagal memperbarui file!" : "Gagal mengirim file!",
description:
err?.response?.data?.message ||
err?.message ||
"Terjadi kesalahan saat memproses file Anda.",
}),
}
);
if (res) {
setIsEditing(false);
setFiles([]);
}
};
const handleDelete = async () => {
if (!confirm("Yakin ingin menghapus submission ini?")) return;
const ok = await remove(submission!.id);
if (ok) { setFiles([]); setIsEditing(false); }
try {
const ok = await remove(submission!.id);
if (ok) {
setFiles([]);
setIsEditing(false);
customToast.success(
"Submission berhasil dihapus!",
"Anda dapat mengirim submission baru kapan saja."
);
}
} catch (err: any) {
customToast.error(
"Gagal menghapus submission!",
err?.response?.data?.message || "Terjadi kesalahan saat menghapus submission."
);
}
};
const handleCancelEdit = () => {
@@ -83,8 +123,6 @@ export default function TaskFormFile({ taskId, projectId, deadline, initialSubmi
);
}
// const currentFileName = submission?.file_path?.split("/").pop();
return (
<form onSubmit={handleSubmit} className="space-y-4">
<input
@@ -118,24 +156,23 @@ export default function TaskFormFile({ taskId, projectId, deadline, initialSubmi
</div>
) : isEditing && submission?.files && submission.files.length > 0 ? (
<div className="flex flex-col items-center justify-center h-full space-y-2 text-center py-6">
<FileText className="w-6 h-6 text-gray-400" />
<p className="text-sm font-semibold text-gray-700">File saat ini:</p>
<div className="space-y-0.5">
{submission.files.map((f) => (
<p key={f.id} className="text-sm font-medium text-red-700">
<div className="flex items-center gap-2 text-[11px]">
<FileText className="w-4 h-4 text-red-700 stroke-[2.5]" />
{f.original_name}
</p>
</div>
))}
</div>
<p className="text-[11px] text-gray-400 font-medium pointer-events-none">
Klik atau drag & drop untuk mengganti file
<p className="text-[14px] text-gray-400 font-medium pointer-events-none">
Upload ulang files yang akan diganti ke dalam submissions, atau klik batal
</p>
</div>
) : (
<div className="flex flex-col items-center justify-center h-full space-y-2 pointer-events-none text-center py-6">
<UploadCloud className="w-8 h-8 text-gray-400 mx-auto stroke-[1.5]" />
<p className="text-sm font-bold text-gray-700">Click to upload <span className="font-medium text-gray-500">or drag & drop</span></p>
<p className="text-[11px] text-gray-400 font-semibold">PDF, DOCX, or ZIP (Max. 10MB)</p>
<p className="text-[11px] text-gray-400 font-semibold">PDF, DOCX, or ZIP (Max.5MB)</p>
</div>
)}
</div>
@@ -4,6 +4,7 @@ import { LuSendHorizontal } from "react-icons/lu";
import SubmitStatusTable from "./SubmissionStatusTable";
import { useSubmission } from "../../../../hooks/useTasks";
import type { TaskSubmissionData } from "../../../../types/task.types";
import { customToast } from "../../../utils/showToast";
interface TaskFormLinkProps {
taskId: string;
@@ -19,18 +20,54 @@ export default function TaskFormLink({ taskId, projectId, deadline, initialSubmi
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!inputLink.trim()) return;
const res = isEditing
? await updateLink(submission!.id, inputLink)
: await submitLink(inputLink);
if (res) { setIsEditing(false); }
if (!inputLink.trim()) {
customToast.error(
"Link belum dibuat",
"Masukkan link untuk mengirim submission"
);
return;
}
await customToast.promise(
isEditing ? updateLink(submission!.id, inputLink) : submitLink(inputLink),
{
loading: isEditing ? "Memperbarui Link..." : "Mengirim Link...",
success: () => ({
title: isEditing ? "Link diperbaharui" : "Link berhasil dikirim",
description: isEditing
? "Perubahan submission Anda telah disimpan."
: "Submission Anda telah berhasil dikirim.",
}),
error: (err) => ({
title: isEditing ? "Gagal memperbarui link!" : "Gagal mengirim link!",
description:
err?.response?.data?.message ||
err?.message ||
"Link yang anda submit gagal dikirim",
}),
}
);
};
const handleDelete = async () => {
if (!confirm("Yakin ingin menghapus submission ini?")) return;
const ok = await remove(submission!.id);
if (ok) setInputLink("");
};
if (!confirm("Yakin ingin menghapus submission ini?")) return;
try {
const ok = await remove(submission!.id);
if (ok) {
setInputLink("");
setIsEditing(false);
customToast.success(
"Submission berhasil dihapus!",
"Anda dapat mengirim submission baru kapan saja."
);
}
} catch (err: any) {
customToast.error(
"Gagal menghapus submission!",
err?.response?.data?.message || "Terjadi kesalahan saat menghapus submission."
);
}
};
if (submission && !isEditing) {
return (