Skip to content

Commit 13436da

Browse files
committed
feat: update UI to match screenshots — stat cards, bar chart, mapping cards
index.html: - Added 4 dashboard stat cards (Overall Maturity, Controls Assessed, Gaps Identified, Framework Maps) to results page - Added side-by-side charts layout (radar + bar chart) - Added chart titles to both chart containers - Added 5 framework summary cards (ISO, NIST, CIS, PCI, NCA) with mapping counts and bilingual descriptions to mappings page - Added page subtitle to mappings page - Updated version to v2.0.0 in footer - Updated footer with GitHub link and @SiteQ8 attribution - Updated page title style.css: - Added .stats-row grid (4-column, responsive to 2/1) - Added .mini-stat-card with hover, label, value, bar, progress - Added .charts-row side-by-side grid layout - Added .chart-title styling - Added .mapping-cards-row grid (5-column, responsive) - Added .mapping-summary-card with accent bar, icon, count badge - Added .page-subtitle styling app.js: - Added renderBarChart() — horizontal bar chart per domain with Level 3 minimum annotation line - Added renderStatCards() — populates stat card values from assessment responses (maturity, controls assessed, gaps count) - Updated radar chart colors to SAMA green (#00a651) - Updated minimum required line to red dashed - Connected new renderers to renderResults() flow
1 parent 63bbcb7 commit 13436da

File tree

3 files changed

+350
-14
lines changed

3 files changed

+350
-14
lines changed

docs/app.js

Lines changed: 112 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,12 @@ function renderResults() {
643643
// Radar chart
644644
renderRadarChart();
645645

646+
// Bar chart
647+
renderBarChart();
648+
649+
// Stat cards
650+
renderStatCards();
651+
646652
// Gap analysis
647653
renderGapAnalysis();
648654

@@ -668,19 +674,19 @@ function renderRadarChart() {
668674
datasets: [{
669675
label: lang === 'ar' ? 'المستوى الحالي' : 'Current Level',
670676
data: frameworkData.domains.map(d => scores[d.domain_id] || 0),
671-
backgroundColor: 'rgba(30, 58, 138, 0.2)',
672-
borderColor: 'rgba(30, 58, 138, 1)',
673-
pointBackgroundColor: 'rgba(30, 58, 138, 1)',
677+
backgroundColor: 'rgba(0, 166, 81, 0.15)',
678+
borderColor: 'rgba(0, 166, 81, 1)',
679+
pointBackgroundColor: 'rgba(0, 166, 81, 1)',
674680
pointBorderColor: '#fff',
675681
pointHoverBackgroundColor: '#fff',
676-
pointHoverBorderColor: 'rgba(30, 58, 138, 1)'
682+
pointHoverBorderColor: 'rgba(0, 166, 81, 1)'
677683
}, {
678684
label: lang === 'ar' ? 'الحد الأدنى المطلوب' : 'Minimum Required',
679685
data: [3, 3, 3, 3],
680-
backgroundColor: 'rgba(5, 150, 105, 0.1)',
681-
borderColor: 'rgba(5, 150, 105, 1)',
686+
backgroundColor: 'rgba(220, 38, 38, 0.05)',
687+
borderColor: 'rgba(220, 38, 38, 0.6)',
682688
borderDash: [5, 5],
683-
pointBackgroundColor: 'rgba(5, 150, 105, 1)',
689+
pointBackgroundColor: 'rgba(220, 38, 38, 0.6)',
684690
pointBorderColor: '#fff'
685691
}]
686692
},
@@ -705,6 +711,105 @@ function renderRadarChart() {
705711
});
706712
}
707713

714+
function renderBarChart() {
715+
const ctx = document.getElementById('barChart');
716+
if (!ctx) return;
717+
718+
const lang = appState.currentLang;
719+
const scores = appState.assessmentData.scores;
720+
721+
if (window.barChartInstance) {
722+
window.barChartInstance.destroy();
723+
}
724+
725+
const domainColors = ['#00a651', '#0891b2', '#059669', '#d97706'];
726+
727+
window.barChartInstance = new Chart(ctx, {
728+
type: 'bar',
729+
data: {
730+
labels: frameworkData.domains.map(d => lang === 'ar' ? d.name_ar : d.name_en),
731+
datasets: [{
732+
label: lang === 'ar' ? 'المستوى الحالي' : 'Current Level',
733+
data: frameworkData.domains.map(d => scores[d.domain_id] || 0),
734+
backgroundColor: domainColors.map(c => c + '80'),
735+
borderColor: domainColors,
736+
borderWidth: 2,
737+
borderRadius: 6
738+
}]
739+
},
740+
options: {
741+
indexAxis: 'y',
742+
responsive: true,
743+
maintainAspectRatio: false,
744+
scales: {
745+
x: {
746+
beginAtZero: true,
747+
max: 5,
748+
ticks: { stepSize: 1 },
749+
grid: { color: 'rgba(0,0,0,0.05)' }
750+
},
751+
y: {
752+
grid: { display: false }
753+
}
754+
},
755+
plugins: {
756+
legend: { display: false },
757+
annotation: {
758+
annotations: {
759+
line1: {
760+
type: 'line',
761+
xMin: 3, xMax: 3,
762+
borderColor: '#dc2626',
763+
borderWidth: 2,
764+
borderDash: [6, 4],
765+
label: {
766+
display: true,
767+
content: lang === 'ar' ? 'الحد الأدنى' : 'Level 3 min.',
768+
position: 'start',
769+
color: '#dc2626',
770+
font: { size: 10 }
771+
}
772+
}
773+
}
774+
}
775+
}
776+
}
777+
});
778+
}
779+
780+
function renderStatCards() {
781+
const scores = appState.assessmentData.scores;
782+
const responses = appState.assessmentData.responses;
783+
const lang = appState.currentLang;
784+
785+
// Overall maturity
786+
const overallEl = document.getElementById('statOverallMaturity');
787+
const maturityBar = document.getElementById('statMaturityBar');
788+
if (overallEl) {
789+
const overall = scores.overall || 0;
790+
overallEl.textContent = overall.toFixed(1);
791+
if (maturityBar) maturityBar.style.width = (overall / 5 * 100) + '%';
792+
}
793+
794+
// Controls assessed
795+
const assessedCount = Object.keys(responses).length;
796+
const totalQuestions = frameworkData.questions ? frameworkData.questions.length : 114;
797+
const assessedEl = document.getElementById('statControlsAssessed');
798+
const totalEl = document.getElementById('statControlsTotal');
799+
const pctEl = document.getElementById('statControlsPct');
800+
if (assessedEl) assessedEl.textContent = assessedCount;
801+
if (totalEl) totalEl.textContent = '/ ' + totalQuestions;
802+
if (pctEl) pctEl.textContent = Math.round(assessedCount / totalQuestions * 100) + '% ' + (lang === 'ar' ? 'مكتمل' : 'complete');
803+
804+
// Gaps (below level 3)
805+
const gaps = frameworkData.questions ? frameworkData.questions.filter(q => {
806+
const r = responses[q.q_id];
807+
return r && r.maturityLevel < 3;
808+
}).length : 0;
809+
const gapsEl = document.getElementById('statGaps');
810+
if (gapsEl) gapsEl.textContent = gaps;
811+
}
812+
708813
function renderGapAnalysis() {
709814
const container = document.getElementById('gapList');
710815
const lang = appState.currentLang;

docs/index.html

Lines changed: 79 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<head>
44
<meta charset="UTF-8">
55
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6-
<title>SAMA CSF Professional Assessment Tool v2.0</title>
6+
<title>SAMA CSF Assessment Tool v2.0 | أداة تقييم إطار SAMA</title>
77
<link rel="preconnect" href="https://fonts.googleapis.com">
88
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
99
<link href="https://fonts.googleapis.com/css2?family=Cairo:wght@400;500;600;700&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
@@ -215,9 +215,42 @@ <h1 class="page-title" data-en="Assessment Results" data-ar="نتائج التق
215215
</div>
216216

217217
<div class="results-overview">
218+
<!-- Dashboard Stat Cards -->
219+
<div class="stats-row" id="statsRow">
220+
<div class="mini-stat-card">
221+
<div class="mini-stat-label" data-en="OVERALL MATURITY" data-ar="النضج العام">النضج العام</div>
222+
<div class="mini-stat-value" id="statOverallMaturity"></div>
223+
<div class="mini-stat-sub">/ 5.0</div>
224+
<div class="mini-stat-bar"><div class="mini-stat-bar-fill" id="statMaturityBar" style="width:0%"></div></div>
225+
</div>
226+
<div class="mini-stat-card">
227+
<div class="mini-stat-label" data-en="CONTROLS ASSESSED" data-ar="الضوابط المُقيّمة">الضوابط المُقيّمة</div>
228+
<div class="mini-stat-value" id="statControlsAssessed">0</div>
229+
<div class="mini-stat-sub" id="statControlsTotal">/ 114</div>
230+
<div class="mini-stat-progress" id="statControlsPct">0% complete</div>
231+
</div>
232+
<div class="mini-stat-card">
233+
<div class="mini-stat-label" data-en="GAPS IDENTIFIED" data-ar="الفجوات المكتشفة">الفجوات المكتشفة</div>
234+
<div class="mini-stat-value mini-stat-danger" id="statGaps">0</div>
235+
<div class="mini-stat-sub" data-en="Below Level 3 minimum" data-ar="أقل من المستوى 3">أقل من المستوى 3</div>
236+
</div>
237+
<div class="mini-stat-card">
238+
<div class="mini-stat-label" data-en="FRAMEWORK MAPS" data-ar="توافق الأطر">توافق الأطر</div>
239+
<div class="mini-stat-value mini-stat-purple">5</div>
240+
<div class="mini-stat-sub">ISO · NIST · CIS · PCI · NCA</div>
241+
</div>
242+
</div>
243+
218244
<div class="score-card overall-score" id="overallScore"></div>
219-
<div class="chart-container" style="height: 400px;">
220-
<canvas id="radarChart"></canvas>
245+
<div class="charts-row">
246+
<div class="chart-container" style="height: 400px;">
247+
<h3 class="chart-title" data-en="Domain Maturity Radar" data-ar="رادار نضج المجالات">رادار نضج المجالات</h3>
248+
<canvas id="radarChart"></canvas>
249+
</div>
250+
<div class="chart-container" style="height: 400px;">
251+
<h3 class="chart-title" data-en="Maturity by Domain" data-ar="النضج حسب المجال">النضج حسب المجال</h3>
252+
<canvas id="barChart"></canvas>
253+
</div>
221254
</div>
222255
</div>
223256

@@ -241,6 +274,46 @@ <h2 class="section-title" data-en="Gap Analysis" data-ar="تحليل الفجو
241274
<section class="section">
242275
<div class="container">
243276
<h1 class="page-title" data-en="Framework Mappings" data-ar="توافق الأطر">توافق الأطر</h1>
277+
<p class="page-subtitle" data-en="See how SAMA CSF aligns with international cybersecurity frameworks" data-ar="اطلع على توافق إطار SAMA مع الأطر الدولية للأمن السيبراني">اطلع على توافق إطار SAMA مع الأطر الدولية للأمن السيبراني</p>
278+
279+
<!-- Framework Summary Cards -->
280+
<div class="mapping-cards-row" id="mappingCardsRow">
281+
<div class="mapping-summary-card" data-accent="#3b82f6">
282+
<div class="mapping-card-accent" style="background:#3b82f6"></div>
283+
<div class="mapping-card-icon" style="color:#3b82f6">ISO</div>
284+
<div class="mapping-card-name">ISO 27001:2022</div>
285+
<div class="mapping-card-desc" data-en="Information Security Management" data-ar="إدارة أمن المعلومات">إدارة أمن المعلومات</div>
286+
<div class="mapping-card-count" style="background:#3b82f610;color:#3b82f6" data-en="~93 mappings" data-ar="~93 توافق">~93 توافق</div>
287+
</div>
288+
<div class="mapping-summary-card" data-accent="#059669">
289+
<div class="mapping-card-accent" style="background:#059669"></div>
290+
<div class="mapping-card-icon" style="color:#059669">NIST</div>
291+
<div class="mapping-card-name">NIST CSF 2.0</div>
292+
<div class="mapping-card-desc" data-en="Cybersecurity Risk Management" data-ar="إدارة مخاطر الأمن السيبراني">إدارة مخاطر الأمن السيبراني</div>
293+
<div class="mapping-card-count" style="background:#05966910;color:#059669" data-en="~87 mappings" data-ar="~87 توافق">~87 توافق</div>
294+
</div>
295+
<div class="mapping-summary-card" data-accent="#7c3aed">
296+
<div class="mapping-card-accent" style="background:#7c3aed"></div>
297+
<div class="mapping-card-icon" style="color:#7c3aed">CIS</div>
298+
<div class="mapping-card-name">CIS Controls v8.1</div>
299+
<div class="mapping-card-desc" data-en="Practical Security Safeguards" data-ar="ضمانات أمنية عملية">ضمانات أمنية عملية</div>
300+
<div class="mapping-card-count" style="background:#7c3aed10;color:#7c3aed" data-en="~76 mappings" data-ar="~76 توافق">~76 توافق</div>
301+
</div>
302+
<div class="mapping-summary-card" data-accent="#dc2626">
303+
<div class="mapping-card-accent" style="background:#dc2626"></div>
304+
<div class="mapping-card-icon" style="color:#dc2626">PCI</div>
305+
<div class="mapping-card-name">PCI-DSS v4.0.1</div>
306+
<div class="mapping-card-desc" data-en="Payment Card Security" data-ar="أمن بطاقات الدفع">أمن بطاقات الدفع</div>
307+
<div class="mapping-card-count" style="background:#dc262610;color:#dc2626" data-en="~64 mappings" data-ar="~64 توافق">~64 توافق</div>
308+
</div>
309+
<div class="mapping-summary-card" data-accent="#d97706">
310+
<div class="mapping-card-accent" style="background:#d97706"></div>
311+
<div class="mapping-card-icon" style="color:#d97706">NCA</div>
312+
<div class="mapping-card-name">NCA ECC</div>
313+
<div class="mapping-card-desc" data-en="Saudi Critical Infrastructure" data-ar="البنية التحتية الحرجة السعودية">البنية التحتية الحرجة السعودية</div>
314+
<div class="mapping-card-count" style="background:#d9770610;color:#d97706" data-en="~58 mappings" data-ar="~58 توافق">~58 توافق</div>
315+
</div>
316+
</div>
244317

245318
<div class="mapping-filters">
246319
<div class="form-group">
@@ -348,16 +421,16 @@ <h3 data-en="About This Tool" data-ar="عن هذه الأداة">عن هذه ا
348421
</div>
349422
<div class="footer-section">
350423
<h3 data-en="Version" data-ar="الإصدار">الإصدار</h3>
351-
<p>v1.0.0</p>
352-
<p data-en="Open Source on GitHub" data-ar="مفتوح المصدر على GitHub">مفتوح المصدر على GitHub</p>
424+
<p>v2.0.0</p>
425+
<p><a href="https://github.com/SiteQ8/sama-csf-assessment" target="_blank" style="color: inherit; text-decoration: underline;" data-en="Open Source on GitHub" data-ar="مفتوح المصدر على GitHub">مفتوح المصدر على GitHub</a></p>
353426
</div>
354427
<div class="footer-section">
355428
<h3 data-en="Educational Disclaimer" data-ar="إخلاء مسؤولية تعليمية">إخلاء مسؤولية تعليمية</h3>
356429
<p data-en="This tool is designed for personal development in cybersecurity and learning SAMA Cybersecurity Framework requirements. This is NOT an official assessment tool and should NOT replace professional compliance audits conducted by qualified experts." data-ar="تم تصميم هذه الأداة للتطوير الشخصي في الأمن السيبراني وتعلم متطلبات إطار عمل SAMA، هذه ليست أداة تقييم رسمية ولا ينبغي أن تحل محل عمليات التدقيق المهنية للامتثال.">تم تصميم هذه الأداة للتطوير الشخصي في الأمن السيبراني وتعلم متطلبات إطار عمل SAMA، هذه ليست أداة تقييم رسمية ولا ينبغي أن تحل محل عمليات التدقيق المهنية للامتثال.</p>
357430
</div>
358431
</div>
359432
<div class="footer-bottom">
360-
<p>&copy; 2025 SAMA CSF Educational Tool. <span data-en="Made for cybersecurity professionals" data-ar="صُنعت لمحترفي الأمن السيبراني">صُنعت لمحترفي الأمن السيبراني</span></p>
433+
<p>&copy; 2026 SAMA CSF Assessment Tool by <a href="https://github.com/SiteQ8" target="_blank" style="color:inherit;text-decoration:underline;">@SiteQ8</a>. <span data-en="Made with ❤️ for the Saudi & GCC Cybersecurity Community" data-ar="صنع بـ ❤️ للمجتمع السيبراني في السعودية والخليج">صنع بـ ❤️ للمجتمع السيبراني في السعودية والخليج</span></p>
361434
</div>
362435
</div>
363436
</footer>

0 commit comments

Comments
 (0)