update: change verifying page to responsive
This commit is contained in:
parent
3aca0aa9dc
commit
68be434d76
|
|
@ -21,6 +21,15 @@ import { ScrollArea } from "@/shadcn/components/ui/scroll-area";
|
||||||
import {
|
import {
|
||||||
Pagination,
|
Pagination,
|
||||||
} from "@/shadcn/components/ui/pagination-assessment";
|
} from "@/shadcn/components/ui/pagination-assessment";
|
||||||
|
import {
|
||||||
|
Sheet,
|
||||||
|
SheetContent,
|
||||||
|
SheetTitle,
|
||||||
|
} from "@/shadcn/components/ui/sheet";
|
||||||
|
import {
|
||||||
|
LeftSheet,
|
||||||
|
LeftSheetContent,
|
||||||
|
} from "@/shadcn/components/ui/leftsheet";
|
||||||
import { useQuery, useMutation } from "@tanstack/react-query";
|
import { useQuery, useMutation } from "@tanstack/react-query";
|
||||||
import {
|
import {
|
||||||
submitAssessmentMutationOptions,
|
submitAssessmentMutationOptions,
|
||||||
|
|
@ -38,11 +47,12 @@ import {
|
||||||
import {
|
import {
|
||||||
getAllVerifiedAspectsAverageScore,
|
getAllVerifiedAspectsAverageScore,
|
||||||
} from "@/modules/assessmentResult/queries/assessmentResultQueries";
|
} from "@/modules/assessmentResult/queries/assessmentResultQueries";
|
||||||
import { TbFlagFilled, TbUpload, TbChevronRight, TbChevronDown } from "react-icons/tb";
|
import { TbLayoutSidebarLeftCollapseFilled, 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 FileSizeValidationModal from "@/modules/assessmentManagement/modals/FileSizeValidationModal";
|
||||||
import { useState, useRef, useEffect } from "react";
|
import { useState, useRef, useEffect } from "react";
|
||||||
|
import AppHeader from "@/components/AppHeader";
|
||||||
|
|
||||||
const getQueryParam = (param: string) => {
|
const getQueryParam = (param: string) => {
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
|
@ -98,6 +108,21 @@ export default function AssessmentPage() {
|
||||||
const [currentPagePerSubAspect, setCurrentPagePerSubAspect] = useState<{ [subAspectId: string]: number }>({});
|
const [currentPagePerSubAspect, setCurrentPagePerSubAspect] = useState<{ [subAspectId: string]: number }>({});
|
||||||
const currentPage = currentPagePerSubAspect[selectedSubAspectId || ""] || 1;
|
const currentPage = currentPagePerSubAspect[selectedSubAspectId || ""] || 1;
|
||||||
const questionsPerPage = 10;
|
const questionsPerPage = 10;
|
||||||
|
const [isMobile, setIsMobile] = useState(window.innerWidth <= 768); // Check for mobile screen
|
||||||
|
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
|
||||||
|
const [openNavbar, setOpenNavbar] = useState(false);
|
||||||
|
const [isLeftSidebarOpen, setIsLeftSidebarOpen] = useState(false);
|
||||||
|
|
||||||
|
const toggleLeftSidebar = () => setIsLeftSidebarOpen(!isLeftSidebarOpen);
|
||||||
|
|
||||||
|
const toggle = () => {
|
||||||
|
setOpenNavbar((prevState) => !prevState);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Adjust layout on screen resize
|
||||||
|
window.addEventListener('resize', () => {
|
||||||
|
setIsMobile(window.innerWidth <= 768);
|
||||||
|
});
|
||||||
|
|
||||||
// Fetch aspects and sub-aspects
|
// Fetch aspects and sub-aspects
|
||||||
const aspectsQuery = useQuery({
|
const aspectsQuery = useQuery({
|
||||||
|
|
@ -587,62 +612,123 @@ console.log("unanswered questions:", unanswered.length);
|
||||||
|
|
||||||
{/* LEFT-SIDE */}
|
{/* LEFT-SIDE */}
|
||||||
{/* Aspek dan Sub-Aspek */}
|
{/* Aspek dan Sub-Aspek */}
|
||||||
<div className="fixed h-screen w-64 overflow-auto">
|
<AppHeader
|
||||||
<Flex direction="column" gap="xs" className="w-64">
|
openNavbar={isLeftSidebarOpen}
|
||||||
<div className="space-y-2">
|
toggle={toggleLeftSidebar}
|
||||||
{/* Aspek */}
|
toggleLeftSidebar={toggleLeftSidebar}
|
||||||
{aspectsQuery.data?.data
|
/>
|
||||||
.filter((aspect) =>
|
|
||||||
aspect.subAspects.some((subAspect) =>
|
{/* Sidebar for Mobile */}
|
||||||
data?.data.some((question) => question.subAspectId === subAspect.id)
|
{isMobile && (
|
||||||
)
|
<LeftSheet open={isLeftSidebarOpen} onOpenChange={(open) => setIsLeftSidebarOpen(open)}>
|
||||||
)
|
<LeftSheetContent className="h-full w-75 overflow-auto">
|
||||||
.map((aspect) => (
|
<Label className="text-gray-800 p-5 text-sm font-normal">Aspek Menu</Label>
|
||||||
<div
|
<Flex direction="column" gap="xs" className="w-64">
|
||||||
key={aspect.id}
|
<div className="space-y-2">
|
||||||
className="p-2 "
|
{/* Aspek */}
|
||||||
>
|
{aspectsQuery.data?.data
|
||||||
<div
|
.filter((aspect) =>
|
||||||
className="flex justify-between cursor-pointer"
|
aspect.subAspects.some((subAspect) =>
|
||||||
onClick={() => toggleAspect(aspect.id)}
|
data?.data.some((question) => question.subAspectId === subAspect.id)
|
||||||
>
|
)
|
||||||
<div className="text-sm font-bold px-3">{aspect.name}</div>
|
)
|
||||||
<div>
|
.map((aspect) => (
|
||||||
{openAspects[aspect.id] ? (
|
<div key={aspect.id} className="p-2 mt-4">
|
||||||
<TbChevronDown size={25} />
|
<div
|
||||||
) : (
|
className="flex justify-between cursor-pointer"
|
||||||
<TbChevronRight size={25} />
|
onClick={() => toggleAspect(aspect.id)}
|
||||||
|
>
|
||||||
|
<div className="text-sm font-bold px-3">{aspect.name}</div>
|
||||||
|
<div>
|
||||||
|
{openAspects[aspect.id] ? (
|
||||||
|
<TbChevronDown size={25} />
|
||||||
|
) : (
|
||||||
|
<TbChevronRight size={25} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Sub-Aspek */}
|
||||||
|
{openAspects[aspect.id] && (
|
||||||
|
<div className="mt-2 space-y-2">
|
||||||
|
{aspect.subAspects
|
||||||
|
.filter((subAspect) =>
|
||||||
|
data?.data.some((question) => question.subAspectId === subAspect.id)
|
||||||
|
)
|
||||||
|
.map((subAspect) => (
|
||||||
|
<div
|
||||||
|
key={subAspect.id}
|
||||||
|
className={`flex justify-between cursor-pointer p-2 px-6 rounded-sm transition-colors duration-150 ${selectedSubAspectId === subAspect.id ? 'text-black font-medium bg-gray-200' : 'text-gray-500'}`}
|
||||||
|
onClick={() => setSelectedSubAspectId(subAspect.id)}
|
||||||
|
>
|
||||||
|
<div className="text-xs">{subAspect.name}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
))}
|
||||||
|
</div>
|
||||||
|
</Flex>
|
||||||
|
</LeftSheetContent>
|
||||||
|
</LeftSheet>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Sub-Aspek */}
|
{/* Sidebar for Desktop (Always Visible) */}
|
||||||
{openAspects[aspect.id] && (
|
<div className="hidden md:block fixed h-screen w-64 overflow-auto">
|
||||||
<div className="mt-2 space-y-2">
|
<Label className="text-gray-800 p-5 text-sm font-normal">Aspek Menu</Label>
|
||||||
{aspect.subAspects
|
<Flex direction="column" gap="xs" className="w-64">
|
||||||
.filter((subAspect) =>
|
<div className="space-y-2">
|
||||||
data?.data.some((question) => question.subAspectId === subAspect.id)
|
{/* Aspek */}
|
||||||
)
|
{aspectsQuery.data?.data
|
||||||
.map((subAspect) => (
|
.filter((aspect) =>
|
||||||
<div
|
aspect.subAspects.some((subAspect) =>
|
||||||
key={subAspect.id}
|
data?.data.some((question) => question.subAspectId === subAspect.id)
|
||||||
className={`flex justify-between cursor-pointer p-2 px-6 rounded-sm transition-colors duration-150 ${selectedSubAspectId === subAspect.id ? 'text-black font-medium bg-gray-200' : 'text-gray-500'}`}
|
)
|
||||||
onClick={() => setSelectedSubAspectId(subAspect.id)}
|
)
|
||||||
>
|
.map((aspect) => (
|
||||||
<div className="text-xs">{subAspect.name}</div>
|
<div key={aspect.id} className="p-2 ">
|
||||||
</div>
|
<div
|
||||||
))}
|
className="flex justify-between cursor-pointer"
|
||||||
|
onClick={() => toggleAspect(aspect.id)}
|
||||||
|
>
|
||||||
|
<div className="text-sm font-bold px-3">{aspect.name}</div>
|
||||||
|
<div>
|
||||||
|
{openAspects[aspect.id] ? (
|
||||||
|
<TbChevronDown size={25} />
|
||||||
|
) : (
|
||||||
|
<TbChevronRight size={25} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
</div>
|
{/* Sub-Aspek */}
|
||||||
))}
|
{openAspects[aspect.id] && (
|
||||||
</div>
|
<div className="mt-2 space-y-2">
|
||||||
</Flex>
|
{aspect.subAspects
|
||||||
</div>
|
.filter((subAspect) =>
|
||||||
|
data?.data.some((question) => question.subAspectId === subAspect.id)
|
||||||
|
)
|
||||||
|
.map((subAspect) => (
|
||||||
|
<div
|
||||||
|
key={subAspect.id}
|
||||||
|
className={`flex justify-between cursor-pointer p-2 px-6 rounded-sm transition-colors duration-150 ${selectedSubAspectId === subAspect.id ? 'text-black font-medium bg-gray-200' : 'text-gray-500'}`}
|
||||||
|
onClick={() => setSelectedSubAspectId(subAspect.id)}
|
||||||
|
>
|
||||||
|
<div className="text-xs">{subAspect.name}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Flex>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* MIDDLE */}
|
{/* MIDDLE */}
|
||||||
{/* Pertanyaan */}
|
{/* Pertanyaan */}
|
||||||
<div className="ml-64 mr-60 flex-1 overflow-y-auto h-full">
|
<div className="ml-0 md:ml-64 mr-0 md:mr-60 flex-1 overflow-y-auto h-full">
|
||||||
<Stack gap="sm" style={{ flex: 1 }}>
|
<Stack gap="sm" style={{ flex: 1 }}>
|
||||||
<Text className="text-2xl font-bold ml-6">
|
<Text className="text-2xl font-bold ml-6">
|
||||||
Harap menjawab semua pertanyaan yang tersedia
|
Harap menjawab semua pertanyaan yang tersedia
|
||||||
|
|
@ -664,39 +750,35 @@ console.log("unanswered questions:", unanswered.length);
|
||||||
className="space-y-4"
|
className="space-y-4"
|
||||||
>
|
>
|
||||||
<Stack gap="sm">
|
<Stack gap="sm">
|
||||||
<Flex justify="space-between" align="flex-start" style={{ width: "100%" }}>
|
<div className="grid grid-cols-[auto_1fr_auto] gap-2 w-full items-start">
|
||||||
{/* Question */}
|
{/* Question Number */}
|
||||||
<Text className="font-bold mx-3 p-1 text-sm">{startIndex + index + 1}.</Text>
|
<Text className="font-bold p-2 text-sm">{startIndex + index + 1}.</Text>
|
||||||
<div className="flex-grow">
|
|
||||||
<Text className="font-bold break-words text-sm p-1">
|
|
||||||
{question.questionText}
|
|
||||||
</Text>
|
|
||||||
</div>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
{/* Opsi Radio Button */}
|
{/* Question Text */}
|
||||||
|
<Text className="font-bold break-words text-sm p-2">{question.questionText}</Text>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Radio Button Options */}
|
||||||
{question.options?.length > 0 ? (
|
{question.options?.length > 0 ? (
|
||||||
<div className="mx-11">
|
<div className="mx-11">
|
||||||
|
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
value={answers[questionId] || ""}
|
value={answers[question.questionId] || ""}
|
||||||
onValueChange={(value) => handleAnswerChange(questionId, value)}
|
onValueChange={(value) => handleAnswerChange(question.questionId, value)}
|
||||||
className="flex flex-col gap-2"
|
className="flex flex-col gap-2"
|
||||||
>
|
>
|
||||||
{question.options.map((option: any) => (
|
{question.options.map((option: any) => (
|
||||||
<div
|
<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-4 rounded-lg p-3 text-sm ${
|
className={`cursor-pointer transition-transform transform hover:scale-105 shadow-md hover:shadow-lg flex items-center border-4 rounded-lg p-3 text-sm ${answers[question.questionId] === option.optionId
|
||||||
answers[questionId] === option.optionId
|
? "bg-[--primary-color] text-white border-[--primary-color]"
|
||||||
? "bg-[--primary-color] text-white border-[--primary-color]"
|
: "bg-gray-200 text-black border-gray-200"
|
||||||
: "bg-gray-200 text-black border-gray-200"
|
}`}
|
||||||
}`}
|
onClick={() => handleAnswerChange(question.questionId, option.optionId)}
|
||||||
onClick={() => handleAnswerChange(questionId, option.optionId)}
|
|
||||||
>
|
>
|
||||||
<RadioGroupItem
|
<RadioGroupItem
|
||||||
value={option.optionId}
|
value={option.optionId}
|
||||||
id={option.optionId}
|
id={option.optionId}
|
||||||
checked={answers[questionId] === option.optionId}
|
checked={answers[question.questionId] === option.optionId}
|
||||||
className="bg-white checked:bg-white checked:border-[--primary-color] pointer-events-none rounded-full"
|
className="bg-white checked:bg-white checked:border-[--primary-color] pointer-events-none rounded-full"
|
||||||
/>
|
/>
|
||||||
<Label
|
<Label
|
||||||
|
|
@ -713,24 +795,16 @@ console.log("unanswered questions:", unanswered.length);
|
||||||
<Text color="red">Tidak ada opsi untuk pertanyaan ini.</Text>
|
<Text color="red">Tidak ada opsi untuk pertanyaan ini.</Text>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Textarea */}
|
{/* Textarea for additional information */}
|
||||||
<div className="mx-11">
|
<div className="mx-11">
|
||||||
<Textarea
|
<Textarea
|
||||||
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 Size Validation Modal */}
|
{/* Divider between questions */}
|
||||||
<FileSizeValidationModal
|
|
||||||
opened={modalOpenFileSize}
|
|
||||||
onClose={() => setModalOpenFileSize(false)}
|
|
||||||
fileName={exceededFileName}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* 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" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -740,11 +814,175 @@ console.log("unanswered questions:", unanswered.length);
|
||||||
})
|
})
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
|
{/* Pagination for mobile */}
|
||||||
|
<div className="md:hidden mb-4 flex justify-center">
|
||||||
|
<Pagination
|
||||||
|
page={currentPage}
|
||||||
|
totalPages={totalPages}
|
||||||
|
onPageChange={(newPage) => {
|
||||||
|
if (selectedSubAspectId) {
|
||||||
|
handlePageChange(selectedSubAspectId, newPage);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text className="text-sm m-0">Halaman {currentPage} dari {totalPages}</Text>
|
||||||
|
</Pagination>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* RIGHT-SIDE */}
|
{/* RIGHT-SIDE */}
|
||||||
{/* Navigasi dan Pagination */}
|
{/* Navigasi dan Pagination */}
|
||||||
<div className="fixed h-screen right-0 w-60 overflow-auto mr-4">
|
{isMobile && (
|
||||||
|
<button
|
||||||
|
onClick={() => setIsSidebarOpen(!isSidebarOpen)}
|
||||||
|
className="fixed bottom-4 right-4 bg-black text-white p-3 rounded-xl shadow-md z-50"
|
||||||
|
>
|
||||||
|
<TbLayoutSidebarLeftCollapseFilled size={30} />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Sidebar for mobile (only when toggled) */}
|
||||||
|
<div className="hidden md:block">
|
||||||
|
<Sheet open={isSidebarOpen} onOpenChange={(open) => setIsSidebarOpen(open)}>
|
||||||
|
<SheetContent className="h-full w-30 overflow-auto">
|
||||||
|
<SheetTitle className="font-medium text-lg text-gray-800 mb-2">
|
||||||
|
Nomor Soal
|
||||||
|
</SheetTitle>
|
||||||
|
|
||||||
|
{/* Navigasi (Number of Questions) */}
|
||||||
|
<div className="grid grid-cols-5 gap-2 pb-3">
|
||||||
|
{Array.from({ length: totalQuestionsInSubAspect }).map((_, i) => {
|
||||||
|
const questionNumber = startIndex + i + 1;
|
||||||
|
const questionId = filteredQuestions[i]?.questionId;
|
||||||
|
|
||||||
|
return questionId ? (
|
||||||
|
<div key={questionId} className="flex justify-center relative">
|
||||||
|
<button
|
||||||
|
className={`
|
||||||
|
w-9 h-9 border rounded-sm flex items-center justify-center relative text-md
|
||||||
|
${answers[questionId] && flaggedQuestions[questionId] ? "bg-white text-black" : ""}
|
||||||
|
${answers[questionId] && !flaggedQuestions[questionId] ? "bg-[--primary-color] text-white" : ""}
|
||||||
|
${!answers[questionId] && !flaggedQuestions[questionId] ? "bg-transparent text-black" : ""}
|
||||||
|
${flaggedQuestions[questionId] ? "border-gray-50" : ""}
|
||||||
|
`}
|
||||||
|
onClick={() => scrollToQuestion(questionId)}
|
||||||
|
>
|
||||||
|
{questionNumber}
|
||||||
|
</button>
|
||||||
|
{flaggedQuestions[questionId] && (
|
||||||
|
<div className="absolute top-0 right-0 w-0 h-0 border-b-[20px] border-b-transparent border-r-[20px] border-r-red-600 rounded-tr-sm" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
) : null;
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Divider */}
|
||||||
|
<div className="border-t-2 border-gray-300 my-4" />
|
||||||
|
|
||||||
|
{/* Skor Aspek dan Sub-Aspek */}
|
||||||
|
<div className="mt-4">
|
||||||
|
<h2 className="text-lg font-extrabold mt-4 mb-2">Nilai Sementara</h2>
|
||||||
|
<div className="max-h-full overflow-hidden">
|
||||||
|
<div>
|
||||||
|
{filteredAspects.length > 0 ? (
|
||||||
|
filteredAspects.map((aspect) => {
|
||||||
|
const aspectScore = parseFloat(aspect.averageScore).toFixed(2);
|
||||||
|
const aspectScoreValue = parseFloat(aspectScore);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div key={aspect.aspectId} className="flex justify-between items-center">
|
||||||
|
<Text className="text-lg text-gray-700 break-words whitespace-normal">{aspect.aspectName}</Text>
|
||||||
|
<Text
|
||||||
|
className={`text-xl font-bold ${
|
||||||
|
aspectScoreValue >= 4.5
|
||||||
|
? "text-green-700"
|
||||||
|
: aspectScoreValue >= 3.5
|
||||||
|
? "text-green-400"
|
||||||
|
: aspectScoreValue >= 2.5
|
||||||
|
? "text-yellow-400"
|
||||||
|
: aspectScoreValue >= 1.5
|
||||||
|
? "text-orange-500"
|
||||||
|
: "text-red-500"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{aspectScore}
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
) : (
|
||||||
|
<Text className="text-base text-gray-700">Data aspek ini kosong</Text>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Divider */}
|
||||||
|
<div className="border-t-2 border-gray-300 my-4" />
|
||||||
|
|
||||||
|
{/* Skor Sub-Aspek */}
|
||||||
|
<div>
|
||||||
|
{filteredSubAspects.length > 0 ? (
|
||||||
|
filteredSubAspects.map((subAspect) => {
|
||||||
|
const subAspectScore = parseFloat(subAspect.averageScore).toFixed(2);
|
||||||
|
const subAspectScoreValue = parseFloat(subAspectScore);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div key={subAspect.subAspectId} className="flex justify-between items-center my-2">
|
||||||
|
<Text className="text-sm text-gray-700 break-words whitespace-normal">{subAspect.subAspectName}</Text>
|
||||||
|
<Text
|
||||||
|
className={`text-sm font-bold ${
|
||||||
|
subAspectScoreValue >= 4.5
|
||||||
|
? "text-green-700"
|
||||||
|
: subAspectScoreValue >= 3.5
|
||||||
|
? "text-green-400"
|
||||||
|
: subAspectScoreValue >= 2.5
|
||||||
|
? "text-yellow-400"
|
||||||
|
: subAspectScoreValue >= 1.5
|
||||||
|
? "text-orange-500"
|
||||||
|
: "text-red-500"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{subAspectScore}
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
) : (
|
||||||
|
<Text className="text-sm text-gray-700">Data sub-aspek ini kosong</Text>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Finish Button */}
|
||||||
|
<div className="mt-4">
|
||||||
|
<Button
|
||||||
|
onClick={handleFinishClick}
|
||||||
|
className="bg-[--primary-color] text-white font-bold rounded-md w-full text-sm"
|
||||||
|
>
|
||||||
|
Selesai
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Finish Assessment Modal */}
|
||||||
|
<FinishAssessmentModal
|
||||||
|
opened={modalOpen}
|
||||||
|
onClose={() => setModalOpen(false)}
|
||||||
|
onConfirm={handleConfirmFinish}
|
||||||
|
assessmentId={assessmentId}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Validation Modal */}
|
||||||
|
<ValidationModal
|
||||||
|
opened={validationModalOpen}
|
||||||
|
onClose={() => setValidationModalOpen(false)}
|
||||||
|
unansweredQuestions={unansweredQuestions}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</SheetContent>
|
||||||
|
</Sheet>
|
||||||
|
</div>
|
||||||
|
{/* Sidebar for desktop (always visible) */}
|
||||||
|
<div className="hidden md:block fixed h-screen right-0 w-60 overflow-auto mr-4">
|
||||||
<Flex direction="column" gap="xs" className="mx-4">
|
<Flex direction="column" gap="xs" className="mx-4">
|
||||||
<Text className="font-medium text-lg text-gray-800 mb-2">
|
<Text className="font-medium text-lg text-gray-800 mb-2">
|
||||||
Nomor Soal
|
Nomor Soal
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user