Pull Request branch dev-clone to main #1
|
|
@ -4,14 +4,15 @@ import {
|
||||||
Flex,
|
Flex,
|
||||||
Pagination,
|
Pagination,
|
||||||
Stack,
|
Stack,
|
||||||
Radio,
|
|
||||||
Text,
|
Text,
|
||||||
Textarea,
|
|
||||||
Loader,
|
Loader,
|
||||||
ActionIcon,
|
ActionIcon,
|
||||||
CloseButton,
|
CloseButton,
|
||||||
Group,
|
Group,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
|
import { Textarea } from "@/shadcn/components/ui/textarea";
|
||||||
|
import { Label } from "@/shadcn/components/ui/label";
|
||||||
|
import { RadioGroup, RadioGroupItem } from "@/shadcn/components/ui/radio-group";
|
||||||
import { useQuery, useMutation } from "@tanstack/react-query";
|
import { useQuery, useMutation } from "@tanstack/react-query";
|
||||||
import {
|
import {
|
||||||
submitAssessmentMutationOptions,
|
submitAssessmentMutationOptions,
|
||||||
|
|
@ -26,6 +27,7 @@ import {
|
||||||
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 ValidationModal from "@/modules/assessmentManagement/modals/ValidationModal";
|
import ValidationModal from "@/modules/assessmentManagement/modals/ValidationModal";
|
||||||
|
import FileSizeValidationModal from "@/modules/assessmentManagement/modals/FileSizeValidationModal";
|
||||||
import { useState, useRef, useEffect } from "react";
|
import { useState, useRef, useEffect } from "react";
|
||||||
|
|
||||||
const getQueryParam = (param: string) => {
|
const getQueryParam = (param: string) => {
|
||||||
|
|
@ -62,6 +64,7 @@ export default function AssessmentPage() {
|
||||||
}>({});
|
}>({});
|
||||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||||
const [modalOpen, setModalOpen] = useState(false);
|
const [modalOpen, setModalOpen] = useState(false);
|
||||||
|
const [modalOpenFileSize, setModalOpenFileSize] = useState(false);
|
||||||
const [selectedAspectId, setSelectedAspectId] = useState<string | null>(null);
|
const [selectedAspectId, setSelectedAspectId] = useState<string | null>(null);
|
||||||
const [selectedSubAspectId, setSelectedSubAspectId] = useState<string | null>(null);
|
const [selectedSubAspectId, setSelectedSubAspectId] = useState<string | null>(null);
|
||||||
const [assessmentId, setAssessmentId] = useState<string | null>(null);
|
const [assessmentId, setAssessmentId] = useState<string | null>(null);
|
||||||
|
|
@ -70,6 +73,7 @@ export default function AssessmentPage() {
|
||||||
const [uploadedFiles, setUploadedFiles] = useState<{ [key: string]: File | null }>({});
|
const [uploadedFiles, setUploadedFiles] = useState<{ [key: string]: File | null }>({});
|
||||||
const [unansweredQuestions, setUnansweredQuestions] = useState(0);
|
const [unansweredQuestions, setUnansweredQuestions] = useState(0);
|
||||||
const [validationModalOpen, setValidationModalOpen] = useState(false);
|
const [validationModalOpen, setValidationModalOpen] = useState(false);
|
||||||
|
const [exceededFileName, setExceededFileName] = useState("");
|
||||||
|
|
||||||
// Fetch aspects and sub-aspects
|
// Fetch aspects and sub-aspects
|
||||||
const aspectsQuery = useQuery({
|
const aspectsQuery = useQuery({
|
||||||
|
|
@ -380,13 +384,26 @@ export default function AssessmentPage() {
|
||||||
});
|
});
|
||||||
}, [assessmentId]);
|
}, [assessmentId]);
|
||||||
|
|
||||||
|
// Max file size in bytes (25 MB)
|
||||||
|
const MAX_FILE_SIZE = 25 * 1024 * 1024;
|
||||||
|
|
||||||
const handleDrop = (event: React.DragEvent<HTMLDivElement>, question: { questionId: string }) => {
|
const handleDrop = (event: React.DragEvent<HTMLDivElement>, question: { questionId: string }) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
setDragActive(false);
|
setDragActive(false);
|
||||||
const droppedFiles = Array.from(event.dataTransfer.files);
|
const droppedFiles = Array.from(event.dataTransfer.files);
|
||||||
|
|
||||||
if (droppedFiles.length > 0) {
|
if (droppedFiles.length > 0) {
|
||||||
|
const file = droppedFiles[0];
|
||||||
|
|
||||||
|
// Validate file size
|
||||||
|
if (file.size > MAX_FILE_SIZE) {
|
||||||
|
setExceededFileName(file.name); // Simpan nama file yang melebihi ukuran
|
||||||
|
setModalOpenFileSize(true); // Tampilkan modal
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('file', droppedFiles[0]); // Hanya menyertakan file pertama
|
formData.append('file', file); // Hanya menyertakan file pertama
|
||||||
|
|
||||||
// Pastikan assessmentId tidak null sebelum menambahkannya ke FormData
|
// Pastikan assessmentId tidak null sebelum menambahkannya ke FormData
|
||||||
if (assessmentId) {
|
if (assessmentId) {
|
||||||
|
|
@ -409,13 +426,14 @@ export default function AssessmentPage() {
|
||||||
// Simpan file dalam state dan local storage menggunakan questionId dan assessmentId sebagai kunci
|
// Simpan file dalam state dan local storage menggunakan questionId dan assessmentId sebagai kunci
|
||||||
setUploadedFiles(prev => ({
|
setUploadedFiles(prev => ({
|
||||||
...prev,
|
...prev,
|
||||||
[question.questionId]: droppedFiles[0], // Simpan file berdasarkan questionId
|
[question.questionId]: file, // Simpan file berdasarkan questionId
|
||||||
}));
|
}));
|
||||||
|
|
||||||
localStorage.setItem(`uploadedFile_${assessmentId}_${question.questionId}`, JSON.stringify({
|
localStorage.setItem(`uploadedFile_${assessmentId}_${question.questionId}`, JSON.stringify({
|
||||||
name: droppedFiles[0].name,
|
name: file.name,
|
||||||
type: droppedFiles[0].type,
|
type: file.type,
|
||||||
lastModified: droppedFiles[0].lastModified,
|
lastModified: file.lastModified,
|
||||||
})); // Simpan info file ke local storage
|
}));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -429,8 +447,17 @@ export default function AssessmentPage() {
|
||||||
if (event.target.files) {
|
if (event.target.files) {
|
||||||
const fileArray = Array.from(event.target.files);
|
const fileArray = Array.from(event.target.files);
|
||||||
if (fileArray.length > 0) {
|
if (fileArray.length > 0) {
|
||||||
|
const file = fileArray[0];
|
||||||
|
|
||||||
|
// Validate file size
|
||||||
|
if (file.size > MAX_FILE_SIZE) {
|
||||||
|
setExceededFileName(file.name); // Simpan nama file yang melebihi ukuran
|
||||||
|
setModalOpenFileSize(true); // Tampilkan modal
|
||||||
|
return; // Hentikan eksekusi fungsi jika ukuran file melebihi batas
|
||||||
|
}
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('file', fileArray[0]); // Hanya menyertakan file pertama
|
formData.append('file', file); // Hanya menyertakan file pertama
|
||||||
|
|
||||||
// Tambahkan assessmentId ke FormData
|
// Tambahkan assessmentId ke FormData
|
||||||
if (assessmentId) {
|
if (assessmentId) {
|
||||||
|
|
@ -453,13 +480,14 @@ export default function AssessmentPage() {
|
||||||
// Simpan file dalam state dan local storage menggunakan questionId dan assessmentId sebagai kunci
|
// Simpan file dalam state dan local storage menggunakan questionId dan assessmentId sebagai kunci
|
||||||
setUploadedFiles(prev => ({
|
setUploadedFiles(prev => ({
|
||||||
...prev,
|
...prev,
|
||||||
[question.questionId]: fileArray[0], // Simpan file berdasarkan questionId
|
[question.questionId]: file, // Simpan file berdasarkan questionId
|
||||||
}));
|
}));
|
||||||
|
|
||||||
localStorage.setItem(`uploadedFile_${assessmentId}_${question.questionId}`, JSON.stringify({
|
localStorage.setItem(`uploadedFile_${assessmentId}_${question.questionId}`, JSON.stringify({
|
||||||
name: fileArray[0].name,
|
name: file.name,
|
||||||
type: fileArray[0].type,
|
type: file.type,
|
||||||
lastModified: fileArray[0].lastModified,
|
lastModified: file.lastModified,
|
||||||
})); // Simpan info file ke local storage
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -652,33 +680,36 @@ export default function AssessmentPage() {
|
||||||
{/* Opsi Radio Button */}
|
{/* Opsi Radio Button */}
|
||||||
{question.options?.length > 0 ? (
|
{question.options?.length > 0 ? (
|
||||||
<div className="mx-11">
|
<div className="mx-11">
|
||||||
<Radio.Group value={answers[question.questionId] || ""}>
|
<RadioGroup
|
||||||
<div className="flex flex-col gap-2">
|
value={answers[question.questionId] || ""}
|
||||||
|
onValueChange={(value) => handleAnswerChange(question.questionId, value)}
|
||||||
|
className="flex flex-col gap-2"
|
||||||
|
>
|
||||||
{question.options.map((option: any) => (
|
{question.options.map((option: any) => (
|
||||||
<label
|
<div
|
||||||
key={option.optionId}
|
key={option.optionId}
|
||||||
className={`cursor-pointer transition-transform transform hover:scale-105 shadow-md hover:shadow-lg flex items-center border rounded-lg p-3 text-sm ${
|
className={`cursor-pointer transition-transform transform hover:scale-105 shadow-md hover:shadow-lg flex items-center border rounded-lg p-3 text-sm ${
|
||||||
answers[question.questionId] === option.optionId
|
answers[question.questionId] === option.optionId
|
||||||
? "bg-blue-500 text-white"
|
? "bg-blue-500 text-white"
|
||||||
: "bg-gray-200 text-black"
|
: "bg-gray-200 text-black"
|
||||||
}`}
|
}`}
|
||||||
onClick={() => document.getElementById(option.optionId)?.click()}
|
onClick={() => handleAnswerChange(question.questionId, option.optionId)}
|
||||||
>
|
>
|
||||||
<Radio
|
<RadioGroupItem
|
||||||
id={option.optionId}
|
|
||||||
className="font-bold"
|
|
||||||
value={option.optionId}
|
value={option.optionId}
|
||||||
label={option.optionText}
|
id={option.optionId}
|
||||||
size="xs"
|
checked={answers[question.questionId] === option.optionId}
|
||||||
radius="xl"
|
className="bg-white checked:bg-white checked:border-blue-500 pointer-events-none rounded-full"
|
||||||
style={{ pointerEvents: "none" }}
|
|
||||||
checked={answers[question.questionId] === option.optionId} // Untuk menampilkan jawaban yang sudah dipilih
|
|
||||||
onChange={() => handleAnswerChange(question.questionId, option.optionId)} // Memanggil handleAnswerChange dengan questionId dan optionId
|
|
||||||
/>
|
/>
|
||||||
</label>
|
<Label
|
||||||
))}
|
htmlFor={option.optionId}
|
||||||
|
className="ml-2 font-bold cursor-pointer flex-1"
|
||||||
|
>
|
||||||
|
{option.optionText}
|
||||||
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
</Radio.Group>
|
))}
|
||||||
|
</RadioGroup>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<Text color="red">Tidak ada opsi untuk pertanyaan ini.</Text>
|
<Text color="red">Tidak ada opsi untuk pertanyaan ini.</Text>
|
||||||
|
|
@ -717,6 +748,9 @@ export default function AssessmentPage() {
|
||||||
<Text className="text-sm text-gray-400">
|
<Text className="text-sm text-gray-400">
|
||||||
PNG, JPG, PDF
|
PNG, JPG, PDF
|
||||||
</Text>
|
</Text>
|
||||||
|
<Text className="text-sm text-gray-400">
|
||||||
|
(Max.File size : 25 MB)
|
||||||
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
</Flex>
|
</Flex>
|
||||||
<input
|
<input
|
||||||
|
|
@ -746,6 +780,13 @@ export default function AssessmentPage() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* File Size Validation Modal */}
|
||||||
|
<FileSizeValidationModal
|
||||||
|
opened={modalOpenFileSize}
|
||||||
|
onClose={() => setModalOpenFileSize(false)}
|
||||||
|
fileName={exceededFileName}
|
||||||
|
/>
|
||||||
|
|
||||||
{/* Garis pembatas setiap soal */}
|
{/* Garis pembatas setiap soal */}
|
||||||
<div>
|
<div>
|
||||||
<hr className="border-t-2 border-gray-300 mx-11 mt-6 mb-6" />
|
<hr className="border-t-2 border-gray-300 mx-11 mt-6 mb-6" />
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user