diff --git a/apps/frontend/src/index.css b/apps/frontend/src/index.css index 76d1a18..4588ab6 100644 --- a/apps/frontend/src/index.css +++ b/apps/frontend/src/index.css @@ -72,8 +72,8 @@ --primary-color: #2555FF; --hover-primary-color: #0032e6; --levelOne-color: #FF2F32; - --levelTwo-color: #FE6939; - --levelThree-color: #EED42D; - --levelFour-color: #17C891; + --levelTwo-color: #DC6E4B; + --levelThree-color: #EBB426; + --levelFour-color: #41CB91; --levelFive-color: #0C7C59; } \ No newline at end of file diff --git a/apps/frontend/src/routes/_dashboardLayout/assessmentResult/index.lazy.tsx b/apps/frontend/src/routes/_dashboardLayout/assessmentResult/index.lazy.tsx index dda4db3..a5b02c0 100644 --- a/apps/frontend/src/routes/_dashboardLayout/assessmentResult/index.lazy.tsx +++ b/apps/frontend/src/routes/_dashboardLayout/assessmentResult/index.lazy.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, useState } from "react"; +import { useEffect, useState } from "react"; import html2pdf from "html2pdf.js"; import useAuth from "@/hooks/useAuth"; import { createLazyFileRoute } from "@tanstack/react-router"; @@ -6,7 +6,6 @@ import { getAllAspectsAverageScore, getAllSubAspectsAverageScore, getAllVerified import { useQuery } from "@tanstack/react-query"; import { getAssessmentResultByIdQueryOptions, getVerifiedAssessmentResultByIdQueryOptions } from "@/modules/assessmentResultsManagement/queries/assessmentResultsManagaementQueries"; import { PieChart, Pie, Label, BarChart, Bar, CartesianGrid, XAxis, YAxis } from "recharts"; -import { PolarAngleAxis, PolarRadiusAxis, PolarGrid, Radar, RadarChart } from "recharts" import { Card, CardContent, @@ -20,8 +19,10 @@ import { ChartTooltipContent, } from "@/shadcn/components/ui/chart" import { aspectQueryOptions } from "@/modules/aspectManagement/queries/aspectQueries"; -import { TbChevronDown, TbChevronLeft, TbChevronUp } from "react-icons/tb"; +import { TbChevronDown, TbChevronLeft, TbChevronUp, TbFileTypePdf } from "react-icons/tb"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/shadcn/components/ui/dropdown-menu"; +import clsx from "clsx"; +import React from "react"; const getQueryParam = (param: string) => { const urlParams = new URLSearchParams(window.location.search); @@ -83,7 +84,6 @@ export default function AssessmentResultPage() { if (score === undefined || score === null) return { color: 'grey' }; let colorVar = '--levelOne-color'; - let textColor = 'white'; let descLevel = '1'; if (score >= 1.50 && score < 2.50) { @@ -92,7 +92,7 @@ export default function AssessmentResultPage() { } else if (score >= 2.50 && score < 3.50) { colorVar = '--levelThree-color'; descLevel = '3'; - } else if (score >= 3.50 && score < 4.49) { + } else if (score >= 3.50 && score < 4.50) { colorVar = '--levelFour-color'; descLevel = '4'; } else if (score >= 4.50 && score <= 5) { @@ -101,17 +101,17 @@ export default function AssessmentResultPage() { } return isBg - ? { backgroundColor: `var(${colorVar})`, color: textColor, descLevel } + ? { backgroundColor: `var(${colorVar})`, descLevel } : { color: `var(${colorVar})`, descLevel }; }; // Warna aspek const aspectsColors = [ - "#DBED9B", - "#FF3F9F", - "#877BDF", - "#CFAF49", - "#5FD4E7", + "#37DCCC", + "#FF8C8C", + "#51D0FD", + "#FEA350", + "#AD8AFC", ]; // Data diagram @@ -190,27 +190,27 @@ export default function AssessmentResultPage() { // Dropdown State const [isOpen, setIsOpen] = useState(false); - const [selectedItem, setSelectedItem] = useState('Hasil Sementara'); + const [selectedItem, setSelectedItem] = useState('Hasil Assessment'); const handleDropdownToggle = () => { setIsOpen((prev) => !prev); }; const handleItemClick = () => { - setSelectedItem(prev => - prev === 'Hasil Sementara' ? 'Hasil Terverifikasi' : 'Hasil Sementara' + setSelectedItem(prev => + prev === 'Hasil Assessment' ? 'Hasil Terverifikasi' : 'Hasil Assessment' ); setIsOpen(false); -}; + }; // Pie Chart Component function PieChartComponent({ chartData, totalScore, chartConfig }: { chartData: { aspectName: string, score: number, fill: string }[], totalScore: number, chartConfig: ChartConfig }) { return ( -
+
{/* Legend */} -
- {chartData.map((entry, index) => ( -
- - {entry.aspectName} -
- ))} +
+
+ {chartData.slice(0, 3).map((entry, index) => ( +
+ + {entry.aspectName} +
+ ))} +
+
+ {chartData.slice(3).map((entry, index) => ( +
+ + {entry.aspectName} +
+ ))} +
); } - // Radar Chart Component - function RadarChartComponent({ chartData, chartConfig }: { chartData: { aspectName: string, score: number }[], chartConfig: ChartConfig }) { - return ( -
- - - { - if (active && payload && payload.length > 0) { - const { aspectName, score } = payload[0].payload; - return ( -
-

{`${aspectName} : ${score}`}

-
- ); - } - return null; - }} - /> - - - - -
-
-
- ); - } - // Bar Chart Component function BarChartComponent({ barChartData, barChartConfig }: { barChartData: { subAspectName: string, score: number, fill: string, aspectId: string, aspectName: string }[], barChartConfig: ChartConfig }) { return ( -
+
- + - + value.toFixed(1)} + ticks={[0, 0,5, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5]} + /> { @@ -360,79 +339,76 @@ export default function AssessmentResultPage() { ); } - const handlePrintPDF = async (isSuperAdmin: boolean) => { + const handlePrintPDF = async () => { const pdfContainer = document.getElementById("pdfContainer"); if (pdfContainer) { - // Sembunyikan elemen yang tidak ingin dicetak - const buttonPrint = document.getElementById("button-print"); - const noPrint = document.getElementById("no-print"); - if (buttonPrint) buttonPrint.style.visibility = 'hidden'; - if (noPrint) noPrint.style.visibility = 'hidden'; + // Sembunyikan elemen yang tidak ingin dicetak + const buttonPrint = document.getElementById("button-print"); + const noPrint = document.getElementById("no-print"); + if (buttonPrint) buttonPrint.style.visibility = 'hidden'; + if (noPrint) noPrint.style.visibility = 'hidden'; - const pdfMargin = isSuperAdmin ? [10, 10, 10, -220] : [10, 10, 10, -230]; - const pdfWidth = isSuperAdmin ? 1575 : 1550; - - const options = { - margin: pdfMargin, - image: { type: 'jpeg', quality: 0.98 }, - html2canvas: { - scale: 2, - width: pdfWidth, // Lebar tetap untuk ukuran A4 (landscape) - height: pdfContainer.scrollHeight, // Tinggi dinamis sesuai konten - }, - jsPDF: { - unit: 'pt', - format: 'a4', - orientation: 'portrait', - } - }; - - try { - const pdfBlob: Blob = await html2pdf() - .set(options) - .from(pdfContainer) - .toPdf() - .get('pdf') - .then((pdf: any) => { - pdf.setProperties({ - title: 'Hasil_Asesemen_Level_Maturitas', - }); - return pdf.output('blob'); - }); - - const pdfURL = URL.createObjectURL(pdfBlob); - window.open(pdfURL, '_blank'); - } catch (err) { - console.error("Error generating PDF:", err); - } finally { - // Tampilkan kembali elemen yang disembunyikan - if (buttonPrint) buttonPrint.style.visibility = 'visible'; - if (noPrint) noPrint.style.visibility = 'visible'; + const options = { + margin: [10, 10, 10, -220], + image: { type: 'jpeg', quality: 0.98 }, + html2canvas: { + scale: 2, + width: 1510, + height: pdfContainer.scrollHeight, + }, + jsPDF: { + unit: 'pt', + format: 'a4', + orientation: 'portrait', } + }; + + try { + const pdfBlob: Blob = await html2pdf() + .set(options) + .from(pdfContainer) + .toPdf() + .get('pdf') + .then((pdf: any) => { + pdf.setProperties({ + title: 'Hasil_Asesemen_Level_Maturitas', + }); + return pdf.output('blob'); + }); + + const pdfURL = URL.createObjectURL(pdfBlob); + window.open(pdfURL, '_blank'); + } catch (err) { + console.error("Error generating PDF:", err); + } finally { + // Tampilkan kembali elemen yang disembunyikan + if (buttonPrint) buttonPrint.style.visibility = 'visible'; + if (noPrint) noPrint.style.visibility = 'visible'; + } } -}; + }; return ( -
-

Tingkatan Level Maturitas

-
+
+

Tingkatan Level Maturitas

+
{[ { level: 5, colorVar: '--levelFive-color', title: 'Implementasi Optimal', details: ['Otomatisasi', 'Terintegrasi', 'Membudaya'], textColor: 'white' }, { level: 4, colorVar: '--levelFour-color', title: 'Implementasi Terkelola', details: ['Terorganisir', 'Review Berkala', 'Berkelanjutan'], textColor: 'white' }, { level: 3, colorVar: '--levelThree-color', title: 'Implementasi Terdefinisi', details: ['Terorganisir', 'Konsisten', 'Review Berkala'], textColor: 'white' }, { level: 2, colorVar: '--levelTwo-color', title: 'Implementasi Berulang', details: ['Terorganisir', 'Tidak Konsisten', 'Berulang'], textColor: 'white' }, { level: 1, colorVar: '--levelOne-color', title: 'Implementasi Awal', details: ['Tidak Terukur', 'Tidak Konsisten', 'Risiko Tinggi'], textColor: 'white' } - ].map(({ level, colorVar, title, details, textColor }, index) => ( -
0 ? '-mt-10' : ''}`}> - - - - Level {level} - - -
+ ].map(({ level, colorVar, title, details }) => ( +
+
+

Level {level}

+
+

{title}

{details.map((detail) => (

{detail}

@@ -441,10 +417,37 @@ export default function AssessmentResultPage() {
))}
+ + {/* Total verified score */} +
+ {selectedItem === 'Hasil Assessment' ? ( + <> +
+

Nilai Maturitas

+ {totalScore} +
+
+

Level Maturitas

+ {getScoreStyleClass(Number(totalScore), true).descLevel} +
+ + ) : ( + <> +
+

Nilai Maturitas

+ {totalVerifiedScore} +
+
+

Level Maturitas

+ {getScoreStyleClass(Number(totalVerifiedScore), true).descLevel} +
+ + )} +
- -
+ +
{/* Konten Header */}
{isSuperAdmin ? ( @@ -461,22 +464,16 @@ export default function AssessmentResultPage() {
) : (
-

Cyber Security Maturity Level Dashboard

+

Dasboard Hasil Tingkat Kematangan Keamanan Cyber

Kelola dan Pantau Semua Permohonan Asesmen Dengan Mudah

)}
-
{selectedItem} @@ -489,31 +486,38 @@ export default function AssessmentResultPage() { {isOpen && ( - {selectedItem === 'Hasil Sementara' ? 'Hasil Terverifikasi' : 'Hasil Sementara'} + {selectedItem === 'Hasil Assessment' ? 'Hasil Terverifikasi' : 'Hasil Assessment'} )}
+
{isSuperAdmin && - -
-

{assessmentResult?.respondentName}

-

{assessmentResult?.position}

-
-
+ +
-

Username

-

{assessmentResult?.username}

+

Nama Responden

+

{assessmentResult?.respondentName}

-

Email

-

{assessmentResult?.email}

+

Posisi

+

{assessmentResult?.position}

+
+
+

Nama Pengguna

+

{assessmentResult?.username}

@@ -525,6 +529,10 @@ export default function AssessmentResultPage() {

Pengalaman Kerja

{assessmentResult?.workExperience}

+
+

Email

+

{assessmentResult?.email}

+
@@ -535,8 +543,6 @@ export default function AssessmentResultPage() {

Alamat

{assessmentResult?.address}

-
-

Tanggal Assessment

@@ -557,8 +563,10 @@ export default function AssessmentResultPage() { )}

+
+
-

Status Verifikasi

+

Status Assesment

{assessmentResult?.statusAssessment}

@@ -567,86 +575,137 @@ export default function AssessmentResultPage() { } {/* Conditional rendering based on selectedItem */} - {selectedItem === 'Hasil Sementara' ? ( + {selectedItem === 'Hasil Assessment' ? ( <> {/* Score Table */} -

Tabel Level Maturitas

- -
- {aspectsData?.data?.map((aspect) => ( -
-
-

{aspect.name}

- {formatScore(getAspectScore(aspect.id))} -
- {aspect.subAspects.map((subAspect) => ( -
-
-

{subAspect.name}

- {formatScore(getSubAspectScore(subAspect.id))} +

Tabel Nilai Hasil Assessment

+ + + + + {aspectsData?.data?.map((aspect) => ( + ))} - - ))} - - - {/* Total score */} -
-
-

Nilai Maturitas:

- {totalScore} -
-
-

Level Maturitas:

- {getScoreStyleClass(Number(totalScore), true).descLevel} -
-
+ + + + {aspectsData && Array.from({ length: Math.max(...aspectsData.data.map(aspect => aspect.subAspects.length)) }).map((_, rowIndex) => ( + + {aspectsData?.data?.map((aspect) => ( + + {/* Sub-aspect Name Column (No Right Border) */} + + {/* Sub-aspect Score Column (No Left Border and w-fit for flexible width) */} + + + ))} + + ))} + +
+
+

{aspect.name}

+ + {formatScore(getAspectScore(aspect.id))} +
- +
+ {aspect.subAspects[rowIndex]?.name || ""} + + {aspect.subAspects[rowIndex] ? formatScore(getSubAspectScore(aspect.subAspects[rowIndex].id)) : ""} +
) : ( <> {/* Verified Result Table */} -

Tabel Level Maturitas Terverifikasi

- -
- {aspectsData?.data?.map((aspect) => ( -
-
-

{aspect.name}

- {formatScore(getVerifiedAspectScore(aspect.id))} -
- {aspect.subAspects.map((subAspect) => ( -
-
-

{subAspect.name}

- {formatScore(getVerifiedSubAspectScore(subAspect.id))} +

Tabel Nilai Hasil Assessment Terverifikasi

+ + + + + {aspectsData?.data?.map((aspect) => ( + ))} - - ))} - - - {/* Total verified score */} -
-
-

Nilai Maturitas:

- {totalScore} -
-
-

Level Maturitas:

- {getScoreStyleClass(Number(totalScore), true).descLevel} -
-
+ + + + {aspectsData && Array.from({ length: Math.max(...aspectsData.data.map(aspect => aspect.subAspects.length)) }).map((_, rowIndex) => ( + + {aspectsData?.data?.map((aspect) => ( + + {/* Sub-aspect Name Column (No Right Border) */} + + {/* Sub-aspect Score Column (No Left Border and w-fit for flexible width) */} + + + ))} + + ))} + +
+
+

{aspect.name}

+ + {formatScore(getVerifiedAspectScore(aspect.id))} +
- +
+ {aspect.subAspects[rowIndex]?.name || ""} + + {aspect.subAspects[rowIndex] ? formatScore(getVerifiedSubAspectScore(aspect.subAspects[rowIndex].id)) : ""} +
)} - + + {/* Bar Chart */} + {selectedItem === 'Hasil Assessment' ? ( + <> + + + Diagram Nilai Hasil Assessment + + + + + + + ) : ( + <> + + + Diagram Nilai Hasil Assessment Terverifikasi + + + + + + + )} + {/* Pie Chart */} - {selectedItem === 'Hasil Sementara' ? ( - + {selectedItem === 'Hasil Assessment' ? ( + Diagram Lingkaran @@ -659,7 +718,7 @@ export default function AssessmentResultPage() { ) : ( - + Diagram Lingkaran Terverifikasi @@ -672,54 +731,6 @@ export default function AssessmentResultPage() { )} - - {/* Radar Chart */} - {selectedItem === 'Hasil Sementara' ? ( - - - Diagram Radar - - - - - - ) : ( - - - Diagram Radar Terverifikasi - - - - - - )} - - - - {/* Bar Chart */} - {selectedItem === 'Hasil Sementara' ? ( - <> - - - Diagram Batang - - - - - - - ) : ( - <> - - - Diagram Batang Terverifikasi - - - - - - - )}