update: change to get all data using format data on get answers query option

This commit is contained in:
abiyasa05 2024-11-08 09:40:20 +07:00
parent 233eef1a99
commit e4473bf15a

View File

@ -24,9 +24,10 @@ import {
import { useQuery, useMutation } from "@tanstack/react-query"; import { useQuery, useMutation } from "@tanstack/react-query";
import { import {
getAnswersQueryOptions, getAnswersQueryOptions,
getAllAnswer,
submitAssessmentMutationOptions, submitAssessmentMutationOptions,
uploadFileMutationOptions, uploadFileMutationOptions,
submitValidationMutationOptions, submitValidationQuery,
submitOptionMutationOptions, submitOptionMutationOptions,
getAverageScoreQueryOptions, getAverageScoreQueryOptions,
fetchAspects, fetchAspects,
@ -35,6 +36,7 @@ import {
} from "@/modules/assessmentManagement/queries/assessmentQueries"; } from "@/modules/assessmentManagement/queries/assessmentQueries";
import { TbFlagFilled, TbUpload, TbChevronRight, TbChevronDown } from "react-icons/tb"; import { TbFlagFilled, TbUpload, TbChevronRight, TbChevronDown } from "react-icons/tb";
import FinishAssessmentModal from "@/modules/assessmentManagement/modals/ConfirmModal"; import FinishAssessmentModal from "@/modules/assessmentManagement/modals/ConfirmModal";
import FileUpload from "@/modules/assessmentManagement/fileUpload/fileUpload";
import ValidationModal from "@/modules/assessmentManagement/modals/ValidationModal"; import ValidationModal from "@/modules/assessmentManagement/modals/ValidationModal";
import FileSizeValidationModal from "@/modules/assessmentManagement/modals/FileSizeValidationModal"; import FileSizeValidationModal from "@/modules/assessmentManagement/modals/FileSizeValidationModal";
import { useState, useRef, useEffect } from "react"; import { useState, useRef, useEffect } from "react";
@ -98,23 +100,6 @@ export default function AssessmentPage() {
getQuestionsAllQueryOptions(page, limit) getQuestionsAllQueryOptions(page, limit)
); );
// Fungsi untuk memeriksa pertanyaan yang belum dijawab
const checkUnansweredQuestions = () => {
// Misalkan data berisi pertanyaan dan jawaban
const unanswered = data?.data.filter(question => {
// Pastikan questionId tidak null dan tidak ada jawaban untuk questionId tersebut
return question.questionId !== null && !answers[question.questionId];
}) || []; // Ganti question.id dengan question.questionId dan tambahkan pengecekan null
setUnansweredQuestions(unanswered.length); // Aman, karena unanswered selalu array
// Jika ada pertanyaan yang belum dijawab, buka modal peringatan
if (unanswered.length > 0) {
setValidationModalOpen(true);
} else {
setModalOpen(true); // Jika tidak ada, buka modal konfirmasi selesai asesmen
}
};
const handleFinishClick = () => { const handleFinishClick = () => {
// Memanggil fungsi untuk memeriksa pertanyaan yang belum dijawab // Memanggil fungsi untuk memeriksa pertanyaan yang belum dijawab
checkUnansweredQuestions(); checkUnansweredQuestions();
@ -172,65 +157,74 @@ export default function AssessmentPage() {
// Fetching answers for the assessment // Fetching answers for the assessment
const { data: answersData } = useQuery( const { data: answersData } = useQuery(
getAnswersQueryOptions(assessmentId || "", page, limit) // Memanggil query untuk mengambil jawaban dengan argumen yang diperlukan getAnswersQueryOptions(assessmentId || "", page, limit),
); );
if (answersData && answersData.data) {
const transformedData = answersData.data.reduce(
(acc: Record<string, string>, item: any) => {
if (item.questionId && item.optionId) {
acc[item.questionId] = item.optionId;
}
return acc;
},
{}
);
} else {
console.error("No data found or data is undefined.");
}
// Effect untuk mengatur answers dari data yang diambil // Effect untuk mengatur answers dari data yang diambil
useEffect(() => { useEffect(() => {
const assessmentId = getQueryParam("id"); // Ambil assessmentId dari query params const assessmentId = getQueryParam("id");
if (!assessmentId) { if (!assessmentId) {
console.error("Assessment ID tidak ditemukan"); console.error("Assessment ID tidak ditemukan");
return; return;
} }
// Ambil jawaban dari localStorage berdasarkan ID assessment // Set answers from `answersData` if data is available
const savedAnswers = localStorage.getItem(`assessmentAnswers_${assessmentId}`); if (answersData && Array.isArray(answersData.data)) {
if (savedAnswers) { const answersFromDatabase = answersData.data.reduce(
setAnswers(JSON.parse(savedAnswers)); // Set state answers dengan data dari local storage (acc: Record<string, string>, item: any) => {
if (item.questionId && item.optionId) {
acc[item.questionId] = item.optionId;
} }
}, []); return acc;
},
{}
);
setAnswers(answersFromDatabase); // Set the transformed data directly to state
} else {
console.error("No data found or data is undefined.");
}
}, [answersData]);
// Mengambil jawaban dari local storage saat komponen dimuat // Fungsi untuk memeriksa pertanyaan yang belum dijawab
useEffect(() => { const checkUnansweredQuestions = () => {
const storedAnswers = localStorage.getItem('assessmentAnswers');
if (storedAnswers) { // Filter pertanyaan yang belum dijawab berdasarkan data `answers`
setAnswers(JSON.parse(storedAnswers)); // Set state answers dengan data dari local storage const unanswered = data?.data.filter(question => {
return question.questionId !== null && !answers[question.questionId];
}) || [];
setUnansweredQuestions(unanswered.length);
// Tampilkan modal berdasarkan jumlah pertanyaan yang belum dijawab
if (unanswered.length > 0) {
setValidationModalOpen(true);
} else {
setModalOpen(true);
} }
}, []); };
const handleConfirmFinish = async (assessmentId: string) => { const handleConfirmFinish = async (assessmentId: string) => {
try { try {
// Cek pertanyaan yang belum dijawab // Skip counting unanswered questions here to prevent duplication
let unansweredCount = 0;
// Cek radio button
data?.data.forEach((question) => {
// Pastikan questionId tidak null sebelum memeriksa answers
if (question.questionId && !answers[question.questionId]) {
unansweredCount += 1;
}
});
// Cek textarea
Object.keys(validationInformation).forEach((key) => {
// Pastikan key tidak null dan tidak ada validasi informasi untuk key tersebut
if (key && !validationInformation[key]) {
unansweredCount += 1;
}
});
if (unansweredCount > 0) {
// Tampilkan modal validasi jika ada pertanyaan yang belum dijawab
setUnansweredQuestions(unansweredCount);
setValidationModalOpen(true);
return;
}
// Memanggil mutation untuk mengubah status asesmen menjadi 'selesai' di backend
const mutation = submitAssessmentMutationOptions(assessmentId); const mutation = submitAssessmentMutationOptions(assessmentId);
const response = await mutation.mutationFn(); const response = await mutation.mutationFn();
// Setelah status diubah, navigasikan ke halaman hasil asesmen // Navigate to results
const newUrl = `/assessmentResult?id=${assessmentId}`; const newUrl = `/assessmentResult?id=${assessmentId}`;
window.history.pushState({}, "", newUrl); window.history.pushState({}, "", newUrl);
console.log("Navigated to:", newUrl); console.log("Navigated to:", newUrl);
@ -238,7 +232,7 @@ export default function AssessmentPage() {
} catch (error) { } catch (error) {
console.error("Error finishing assessment:", error); console.error("Error finishing assessment:", error);
} finally { } finally {
setModalOpen(false); // Menutup modal setelah selesai setModalOpen(false);
} }
}; };
@ -265,44 +259,50 @@ export default function AssessmentPage() {
const currentAspect = aspects.find(aspect => aspect.aspectId === selectedAspectId); const currentAspect = aspects.find(aspect => aspect.aspectId === selectedAspectId);
const filteredSubAspects = currentAspect ? currentAspect.subAspects : []; const filteredSubAspects = currentAspect ? currentAspect.subAspects : [];
// Inisialisasi flaggedQuestions dari localStorage saat komponen dimuat // Inisialisasi flaggedQuestions dari database saat komponen dimuat
useEffect(() => { useEffect(() => {
const savedFlags = localStorage.getItem("flaggedQuestions"); const initialFlagData = answersData?.data.reduce((acc, item) => {
if (savedFlags) { if (item.questionId != null && item.isFlagged != null) {
setFlaggedQuestions(JSON.parse(savedFlags)); acc[item.questionId] = item.isFlagged;
} }
}, []); return acc;
}, {} as Record<string, boolean>);
// Simpan perubahan flag ke localStorage setiap kali flaggedQuestions berubah if (initialFlagData) {
useEffect(() => { setFlaggedQuestions(initialFlagData);
if (Object.keys(flaggedQuestions).length > 0) {
localStorage.setItem("flaggedQuestions", JSON.stringify(flaggedQuestions));
} }
}, [flaggedQuestions]); }, [answersData]);
// Mutation function to toggle flag // Mutation function to toggle flag
const toggleFlagMutation = useMutation<ToggleFlagResponse, Error, string>({ const { mutate: toggleFlag } = useMutation({
mutationFn: toggleFlagAnswer, mutationFn: (questionId: string) => toggleFlagAnswer(questionId),
onSuccess: (response) => { onSuccess: (response) => {
if (response && response.answer) { if (response && response.answer) {
const { answer } = response; const { answer } = response;
setFlaggedQuestions((prevFlags) => { setFlaggedQuestions((prevFlags) => ({
const newFlags = {
...prevFlags, ...prevFlags,
[answer.id]: answer.isFlagged !== null ? answer.isFlagged : false, [answer.id]: answer.isFlagged !== null ? answer.isFlagged : false,
}; }));
// Simpan perubahan ke localStorage
localStorage.setItem("flaggedQuestions", JSON.stringify(newFlags));
return newFlags;
});
} }
}, },
onError: (error) => { onError: (error) => {
console.error("Error toggling flag:", error); console.error("Error toggling flag:", error);
}, },
}); });
// Fungsi untuk toggle flag
const handleToggleFlag = (questionId: string) => {
const newFlagState = !flaggedQuestions[questionId];
// Update flaggedQuestions dan kirim ke server
setFlaggedQuestions((prevFlags) => ({
...prevFlags,
[questionId]: newFlagState,
}));
toggleFlag(questionId);
};
// Usage of the mutation in your component // Usage of the mutation in your component
const submitOptionMutation = useMutation({ const submitOptionMutation = useMutation({
...submitOptionMutationOptions, // Spread the mutation options here ...submitOptionMutationOptions, // Spread the mutation options here
@ -315,37 +315,18 @@ export default function AssessmentPage() {
}, },
}); });
useEffect(() => {
const assessmentId = getQueryParam("id");
if (!assessmentId) {
console.error("Assessment ID tidak ditemukan");
return;
}
// Ambil jawaban dari localStorage berdasarkan ID assessment
const savedAnswers = localStorage.getItem(`assessmentAnswers_${assessmentId}`);
if (savedAnswers) {
setAnswers(JSON.parse(savedAnswers));
}
}, []);
const handleAnswerChange = (questionId: string, optionId: string) => { const handleAnswerChange = (questionId: string, optionId: string) => {
const assessmentId = getQueryParam("id"); const assessmentId = getQueryParam("id");
if (!assessmentId) { if (!assessmentId) {
console.error("Assessment ID tidak ditemukan"); console.error("Assessment ID tidak ditemukan");
return; return;
} }
// Simpan jawaban ke localStorage dengan ID assessment // Update answers in the state
const updatedAnswers = { ...answers, [questionId]: optionId }; const updatedAnswers = { ...answers, [questionId]: optionId };
localStorage.setItem(`assessmentAnswers_${assessmentId}`, JSON.stringify(updatedAnswers)); // Simpan berdasarkan ID assessment
// Update state
setAnswers(updatedAnswers); setAnswers(updatedAnswers);
// Call the mutation to submit the option // Send the updated answer to the backend
submitOptionMutation.mutate({ submitOptionMutation.mutate({
optionId, optionId,
assessmentId, assessmentId,
@ -355,50 +336,77 @@ export default function AssessmentPage() {
}); });
}; };
// Mutation untuk mengirim data ke backend const validationResult = answersData?.data.reduce((acc, item) => {
const { mutate: submitValidation } = useMutation(submitValidationMutationOptions()); if (item.questionId != null && item.validationInformation != null) {
acc[item.questionId] = item.validationInformation;
}
return acc;
}, {} as Record<string, string>);
// Mengambil data dari localStorage saat komponen dimuat // Mengambil data dari database saat komponen dimuat
useEffect(() => { useEffect(() => {
const storedValidationInfo = localStorage.getItem(`validationInfo_${assessmentId}`); if (validationResult) {
if (storedValidationInfo) { setValidationInformation(validationResult);
try {
const parsedValidationInfo = JSON.parse(storedValidationInfo);
setValidationInformation(parsedValidationInfo);
} catch (error) {
console.error("Error parsing validation information:", error);
} }
} }, [answersData, assessmentId]);
}, [assessmentId]);
// Mutation untuk mengirim data ke backend
const { mutate: submitValidation } = useMutation({
mutationFn: (form: {
assessmentId: string;
questionId: string;
validationInformation: string;
}) => submitValidationQuery(form),
onSuccess: () => {
// Tindakan yang diambil setelah berhasil
console.log("Validation updated successfully!");
},
onError: (error) => {
console.error("Error updating validation:", error);
},
});
// Handle perubahan di Textarea // Handle perubahan di Textarea
const handleTextareaChange = (questionId: string, value: string) => { const handleTextareaChange = (questionId: string, value: string) => {
// Memperbarui state validationInformation
setValidationInformation((prev) => ({ setValidationInformation((prev) => ({
...prev, ...prev,
[questionId]: value, [questionId]: value,
})); }));
// Update the localStorage with the new validation information as JSON // Pastikan assessmentId tidak null sebelum mengirimkan data ke server
const updatedValidationInformation = { if (assessmentId) {
...validationInformation, // Kirim data validasi ke server
[questionId]: value,
};
localStorage.setItem(`validationInfo_${assessmentId}`, JSON.stringify(updatedValidationInformation));
// Ensure assessmentId and questionId are not null before submitting
if (assessmentId && questionId) {
// Send the validation data to the server
submitValidation({ submitValidation({
assessmentId, assessmentId,
questionId, questionId,
validationInformation: value, validationInformation: value,
}); });
} else {
console.error("Assessment ID tidak ditemukan");
} }
}; };
// Mutation for file upload // Mutation for file upload
const uploadFileMutation = useMutation(uploadFileMutationOptions()); const uploadFileMutation = useMutation(uploadFileMutationOptions());
// Inisialisasi uploadedFiles dari data yang diterima (answersData)
useEffect(() => {
if (answersData && answersData.data) {
const transformedFileData = answersData.data.reduce((acc, item) => {
if (item.questionId && item.filename) {
acc[item.questionId] = new File([""], item.filename, { type: "application/pdf" });
}
return acc;
}, {} as Record<string, File | null>);
setUploadedFiles(transformedFileData);
}
}, [answersData]);
// Drag and Drop handlers // Drag and Drop handlers
const handleDragOver = (event: React.DragEvent<HTMLDivElement>) => { const handleDragOver = (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault(); event.preventDefault();
@ -409,21 +417,6 @@ export default function AssessmentPage() {
setDragActive(false); setDragActive(false);
}; };
// Load uploaded files from local storage when the component mounts
useEffect(() => {
const keys = Object.keys(localStorage);
keys.forEach((key) => {
if (key.startsWith(`uploadedFile_${assessmentId}_`)) { // Menggunakan assessmentId
const fileData = JSON.parse(localStorage.getItem(key) || '{}');
const questionId = key.replace(`uploadedFile_${assessmentId}_`, ''); // Ambil questionId dari kunci
setUploadedFiles(prev => ({
...prev,
[questionId]: new File([fileData], fileData.name, { type: fileData.type }), // Buat objek File baru
}));
}
});
}, [assessmentId]);
// Max file size in bytes (64 MB) // Max file size in bytes (64 MB)
const MAX_FILE_SIZE = 64 * 1024 * 1024; const MAX_FILE_SIZE = 64 * 1024 * 1024;
@ -437,42 +430,34 @@ export default function AssessmentPage() {
// Validate file size // Validate file size
if (file.size > MAX_FILE_SIZE) { if (file.size > MAX_FILE_SIZE) {
setExceededFileName(file.name); // Simpan nama file yang melebihi ukuran setExceededFileName(file.name);
setModalOpenFileSize(true); // Tampilkan modal setModalOpenFileSize(true);
return; return;
} }
const formData = new FormData(); const formData = new FormData();
formData.append('file', file); // Hanya menyertakan file pertama formData.append('file', file);
// Pastikan assessmentId tidak null sebelum menambahkannya ke FormData
if (assessmentId) { if (assessmentId) {
formData.append('assessmentId', assessmentId); formData.append('assessmentId', assessmentId);
} else { } else {
console.error("assessmentId is null"); console.error("assessmentId is null");
return; // Atau tangani sesuai kebutuhan return;
} }
// Tambahkan questionId ke FormData
if (question.questionId) { if (question.questionId) {
formData.append('questionId', question.questionId); formData.append('questionId', question.questionId);
} else { } else {
console.error("questionId is null"); console.error("questionId is null");
return; // Atau tangani sesuai kebutuhan return;
} }
uploadFileMutation.mutate(formData); // Unggah file uploadFileMutation.mutate(formData); // Upload file
// Simpan file dalam state dan local storage menggunakan questionId dan assessmentId sebagai kunci // Update state to reflect the uploaded file (store the File object, not just the name)
setUploadedFiles(prev => ({ setUploadedFiles(prev => ({
...prev, ...prev,
[question.questionId]: file, // Simpan file berdasarkan questionId [question.questionId]: file, // Store the file itself
}));
localStorage.setItem(`uploadedFile_${assessmentId}_${question.questionId}`, JSON.stringify({
name: file.name,
type: file.type,
lastModified: file.lastModified,
})); }));
} }
}; };
@ -491,53 +476,44 @@ export default function AssessmentPage() {
// Validate file size // Validate file size
if (file.size > MAX_FILE_SIZE) { if (file.size > MAX_FILE_SIZE) {
setExceededFileName(file.name); // Simpan nama file yang melebihi ukuran setExceededFileName(file.name);
setModalOpenFileSize(true); // Tampilkan modal setModalOpenFileSize(true);
return; // Hentikan eksekusi fungsi jika ukuran file melebihi batas return;
} }
const formData = new FormData(); const formData = new FormData();
formData.append('file', file); // Hanya menyertakan file pertama formData.append('file', file);
// Tambahkan assessmentId ke FormData
if (assessmentId) { if (assessmentId) {
formData.append('assessmentId', assessmentId); formData.append('assessmentId', assessmentId);
} else { } else {
console.error("assessmentId is null"); console.error("assessmentId is null");
return; // Atau tangani sesuai kebutuhan return;
} }
// Tambahkan questionId ke FormData
if (question.questionId) { if (question.questionId) {
formData.append('questionId', question.questionId); formData.append('questionId', question.questionId);
} else { } else {
console.error("questionId is null"); console.error("questionId is null");
return; // Atau tangani sesuai kebutuhan return;
} }
uploadFileMutation.mutate(formData); // Unggah file uploadFileMutation.mutate(formData); // Upload file
// Simpan file dalam state dan local storage menggunakan questionId dan assessmentId sebagai kunci // Update state to reflect the uploaded file (store the File object)
setUploadedFiles(prev => ({ setUploadedFiles(prev => ({
...prev, ...prev,
[question.questionId]: file, // Simpan file berdasarkan questionId [question.questionId]: file, // Store the File object, not just the name
}));
localStorage.setItem(`uploadedFile_${assessmentId}_${question.questionId}`, JSON.stringify({
name: file.name,
type: file.type,
lastModified: file.lastModified,
})); }));
} }
} }
}; };
const handleRemoveFile = (question: { questionId: string }) => { const handleRemoveFile = (question: { questionId: string }) => {
setUploadedFiles(prev => ({ setUploadedFiles((prev) => ({
...prev, ...prev,
[question.questionId]: null, // Hapus file yang diunggah untuk pertanyaan ini [question.questionId]: null,
})); }));
localStorage.removeItem(`uploadedFile_${assessmentId}_${question.questionId}`); // Hapus info file dari local storage
}; };
// Function to scroll to the specific question // Function to scroll to the specific question
@ -694,18 +670,7 @@ export default function AssessmentPage() {
{/* Action Icon/Flag */} {/* Action Icon/Flag */}
<ActionIcon <ActionIcon
onClick={() => { onClick={() => handleToggleFlag(questionId)}
setFlaggedQuestions((prevFlags) => {
const newFlags = {
...prevFlags,
[questionId]: !prevFlags[questionId],
};
// Simpan perubahan ke localStorage
localStorage.setItem("flaggedQuestions", JSON.stringify(newFlags));
return newFlags;
});
toggleFlagMutation.mutate(questionId);
}}
title="Tandai" title="Tandai"
className={`m-2 rounded-md border-1 flex items-center justify-center h-7 w-7 ${flaggedQuestions[questionId] ? "border-white bg-red-500" : "border-gray-100 bg-white"}`} className={`m-2 rounded-md border-1 flex items-center justify-center h-7 w-7 ${flaggedQuestions[questionId] ? "border-white bg-red-500" : "border-gray-100 bg-white"}`}
> >
@ -760,70 +725,23 @@ export default function AssessmentPage() {
placeholder="Berikan keterangan terkait jawaban di atas" placeholder="Berikan keterangan terkait jawaban di atas"
value={validationInformation[question.questionId] || ""} value={validationInformation[question.questionId] || ""}
onChange={(event) => handleTextareaChange(question.questionId, event.currentTarget.value)} onChange={(event) => handleTextareaChange(question.questionId, event.currentTarget.value)}
disabled={!answers[question.questionId]}
/> />
</div> </div>
{/* File Upload */} {/* File Upload */}
<div className="mx-11"> <FileUpload
{question.needFile === true && ( question={question}
<div handleFileChange={handleFileChange}
className={`pt-5 pb-5 pr-5 pl-5 border-2 rounded-lg border-dashed ${dragActive ? "bg-gray-100" : "bg-transparent" handleRemoveFile={handleRemoveFile}
} shadow-lg`} uploadedFiles={uploadedFiles}
onDragOver={handleDragOver} dragActive={dragActive}
onDragLeave={handleDragLeave} handleDragOver={handleDragOver}
onDrop={(event) => handleDrop(event, question)} // Mengoper question sebagai argumen handleDragLeave={handleDragLeave}
onClick={handleClick} handleDrop={handleDrop}
> modalOpenFileSize={modalOpenFileSize}
<Flex align="center" justify="space-between" gap="sm"> setModalOpenFileSize={setModalOpenFileSize}
<TbUpload exceededFileName={exceededFileName}
size={24} handleClick={handleClick}
style={{ marginLeft: "8px", marginRight: "8px" }}
/>
<div className="flex-grow text-right">
<Text className="font-bold">
Klik untuk unggah atau geser file disini
</Text>
<Text className="text-sm text-gray-400">
PNG, JPG, PDF
</Text>
<Text className="text-sm text-gray-400">
(Max.File size : 64 MB)
</Text>
</div>
</Flex>
<input
type="file"
ref={fileInputRef}
onChange={(event) => handleFileChange(event, question)} // Mengoper question sebagai argumen
style={{ display: "none" }}
accept="image/png, image/jpeg, application/pdf"
disabled={!answers[question.questionId]}
/>
</div>
)}
</div>
<div className="mx-11 px-1">
{uploadedFiles[question.questionId] && (
<Stack gap="sm" mt="sm">
<Text className="font-bold">File yang diunggah:</Text>
<Group align="center">
<Text>{uploadedFiles[question.questionId]?.name}</Text> {/* Tampilkan nama file yang diunggah */}
<CloseButton
title="Hapus file"
onClick={() => handleRemoveFile(question)} // Mengoper question sebagai argumen
/>
</Group>
</Stack>
)}
</div>
{/* File Size Validation Modal */}
<FileSizeValidationModal
opened={modalOpenFileSize}
onClose={() => setModalOpenFileSize(false)}
fileName={exceededFileName}
/> />
{/* Garis pembatas setiap soal */} {/* Garis pembatas setiap soal */}