|
<!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>PCI DSS Assessment Tracker</title> |
|
<script src="https://cdn.tailwindcss.com"></script> |
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
|
<style> |
|
.progress-bar { |
|
transition: width 0.5s ease-in-out; |
|
} |
|
.requirement-item:hover { |
|
transform: translateY(-2px); |
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); |
|
} |
|
.sidebar { |
|
transition: all 0.3s ease; |
|
} |
|
.tab-content { |
|
display: none; |
|
} |
|
.tab-content.active { |
|
display: block; |
|
} |
|
@media (max-width: 768px) { |
|
.sidebar { |
|
transform: translateX(-100%); |
|
position: absolute; |
|
z-index: 10; |
|
height: 100vh; |
|
} |
|
.sidebar.open { |
|
transform: translateX(0); |
|
} |
|
} |
|
</style> |
|
</head> |
|
<body class="bg-gray-50"> |
|
<div class="flex h-screen overflow-hidden"> |
|
|
|
<div class="sidebar bg-blue-800 text-white w-64 flex-shrink-0"> |
|
<div class="p-4 flex items-center justify-between border-b border-blue-700"> |
|
<h1 class="text-xl font-bold">PCI DSS Tracker</h1> |
|
<button id="sidebarToggle" class="md:hidden"> |
|
<i class="fas fa-times"></i> |
|
</button> |
|
</div> |
|
<div class="p-4"> |
|
<button id="newAssessmentBtn" class="w-full bg-blue-600 hover:bg-blue-700 text-white py-2 px-4 rounded-lg mb-4 flex items-center justify-center"> |
|
<i class="fas fa-plus mr-2"></i> New Assessment |
|
</button> |
|
<div class="mb-6"> |
|
<h3 class="text-sm uppercase font-semibold text-blue-200 mb-2">Projects</h3> |
|
<ul id="projectList" class="space-y-1"> |
|
|
|
</ul> |
|
</div> |
|
<div class="mb-6"> |
|
<h3 class="text-sm uppercase font-semibold text-blue-200 mb-2">Filters</h3> |
|
<div class="space-y-2"> |
|
<label class="flex items-center space-x-2"> |
|
<input type="checkbox" class="form-checkbox" checked> |
|
<span>In Progress</span> |
|
</label> |
|
<label class="flex items-center space-x-2"> |
|
<input type="checkbox" class="form-checkbox" checked> |
|
<span>Completed</span> |
|
</label> |
|
<label class="flex items-center space-x-2"> |
|
<input type="checkbox" class="form-checkbox"> |
|
<span>Overdue</span> |
|
</label> |
|
</div> |
|
</div> |
|
<div> |
|
<h3 class="text-sm uppercase font-semibold text-blue-200 mb-2">Admin</h3> |
|
<ul> |
|
<li> |
|
<button id="adminDashboardBtn" class="w-full text-left hover:bg-blue-700 px-2 py-1 rounded flex items-center"> |
|
<i class="fas fa-cog mr-2"></i> Admin Dashboard |
|
</button> |
|
</li> |
|
</ul> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div class="flex-1 flex flex-col overflow-hidden"> |
|
|
|
<header class="bg-white shadow-sm z-10"> |
|
<div class="flex items-center justify-between p-4"> |
|
<div class="flex items-center"> |
|
<button id="mobileSidebarToggle" class="md:hidden mr-4"> |
|
<i class="fas fa-bars"></i> |
|
</button> |
|
<h2 id="currentProjectTitle" class="text-lg font-semibold">Select a Project</h2> |
|
</div> |
|
<div class="flex items-center space-x-4"> |
|
<div class="relative"> |
|
<input type="text" placeholder="Search..." class="pl-8 pr-4 py-2 border rounded-lg"> |
|
<i class="fas fa-search absolute left-3 top-3 text-gray-400"></i> |
|
</div> |
|
<div class="relative group"> |
|
<div class="w-8 h-8 rounded-full bg-blue-600 flex items-center justify-center text-white cursor-pointer"> |
|
<i class="fas fa-user"></i> |
|
</div> |
|
<div class="absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg py-1 z-50 hidden group-hover:block"> |
|
<a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Profile</a> |
|
<a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Settings</a> |
|
<a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Logout</a> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</header> |
|
|
|
|
|
<main class="flex-1 overflow-y-auto p-4 bg-gray-50"> |
|
|
|
<div id="dashboardView" class="space-y-6"> |
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-4"> |
|
<div class="bg-white p-4 rounded-lg shadow"> |
|
<h3 class="text-gray-500 font-medium">Active Assessments</h3> |
|
<p class="text-3xl font-bold text-blue-600" id="activeAssessmentsCount">0</p> |
|
</div> |
|
<div class="bg-white p-4 rounded-lg shadow"> |
|
<h3 class="text-gray-500 font-medium">Compliance Score</h3> |
|
<div class="flex items-center"> |
|
<p class="text-3xl font-bold text-green-600" id="complianceScore">0%</p> |
|
<div class="ml-4 w-full bg-gray-200 rounded-full h-2.5"> |
|
<div id="complianceBar" class="bg-green-600 h-2.5 rounded-full progress-bar" style="width: 0%"></div> |
|
</div> |
|
</div> |
|
</div> |
|
<div class="bg-white p-4 rounded-lg shadow"> |
|
<h3 class="text-gray-500 font-medium">Upcoming Deadlines</h3> |
|
<p class="text-3xl font-bold text-orange-500" id="upcomingDeadlines">0</p> |
|
</div> |
|
</div> |
|
|
|
<div class="bg-white p-4 rounded-lg shadow"> |
|
<div class="flex justify-between items-center mb-4"> |
|
<h3 class="font-semibold">Recent Assessments</h3> |
|
<button class="text-blue-600 hover:text-blue-800">View All</button> |
|
</div> |
|
<div class="overflow-x-auto"> |
|
<table class="min-w-full divide-y divide-gray-200"> |
|
<thead class="bg-gray-50"> |
|
<tr> |
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Project</th> |
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th> |
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Progress</th> |
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Due Date</th> |
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th> |
|
</tr> |
|
</thead> |
|
<tbody id="recentAssessmentsTable" class="bg-white divide-y divide-gray-200"> |
|
|
|
</tbody> |
|
</table> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div id="assessmentDetailView" class="hidden space-y-6"> |
|
<div class="flex justify-between items-center"> |
|
<button id="backToDashboard" class="text-blue-600 hover:text-blue-800 flex items-center"> |
|
<i class="fas fa-arrow-left mr-2"></i> Back to Dashboard |
|
</button> |
|
<div class="flex space-x-2"> |
|
<button class="bg-blue-600 hover:bg-blue-700 text-white py-2 px-4 rounded-lg"> |
|
<i class="fas fa-file-export mr-2"></i> Export |
|
</button> |
|
<button class="bg-green-600 hover:bg-green-700 text-white py-2 px-4 rounded-lg"> |
|
<i class="fas fa-check mr-2"></i> Mark Complete |
|
</button> |
|
</div> |
|
</div> |
|
|
|
<div class="bg-white p-6 rounded-lg shadow"> |
|
<div class="flex justify-between items-start mb-6"> |
|
<div> |
|
<h2 id="assessmentTitle" class="text-2xl font-bold">Assessment Title</h2> |
|
<div class="flex items-center mt-2 space-x-4"> |
|
<span id="assessmentStatus" class="px-3 py-1 rounded-full text-xs font-semibold bg-blue-100 text-blue-800">In Progress</span> |
|
<span id="assessmentDueDate" class="text-gray-500">Due: 2023-12-31</span> |
|
</div> |
|
</div> |
|
<div class="text-right"> |
|
<p class="text-gray-500">Last Updated</p> |
|
<p id="lastUpdated" class="font-semibold">2023-06-15</p> |
|
</div> |
|
</div> |
|
|
|
<div class="mb-6"> |
|
<div class="flex justify-between mb-1"> |
|
<span class="text-sm font-medium text-gray-700">Overall Compliance</span> |
|
<span id="assessmentCompliance" class="text-sm font-medium text-gray-700">0%</span> |
|
</div> |
|
<div class="w-full bg-gray-200 rounded-full h-2.5"> |
|
<div id="assessmentComplianceBar" class="bg-green-600 h-2.5 rounded-full progress-bar" style="width: 0%"></div> |
|
</div> |
|
</div> |
|
|
|
<div class="mb-6"> |
|
<h3 class="font-semibold mb-3">Assessment Details</h3> |
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4"> |
|
<div> |
|
<p class="text-gray-500">Project Manager</p> |
|
<p id="projectManager" class="font-medium">John Doe</p> |
|
</div> |
|
<div> |
|
<p class="text-gray-500">QSA</p> |
|
<p id="qsaName" class="font-medium">Jane Smith</p> |
|
</div> |
|
<div> |
|
<p class="text-gray-500">Start Date</p> |
|
<p id="startDate" class="font-medium">2023-01-15</p> |
|
</div> |
|
<div> |
|
<p class="text-gray-500">Completion Target</p> |
|
<p id="completionTarget" class="font-medium">2023-12-31</p> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<div> |
|
<div class="flex justify-between items-center mb-4"> |
|
<h3 class="font-semibold">PCI DSS Requirements</h3> |
|
<div class="flex space-x-2"> |
|
<button id="expandAll" class="text-blue-600 hover:text-blue-800 text-sm">Expand All</button> |
|
<button id="collapseAll" class="text-blue-600 hover:text-blue-800 text-sm">Collapse All</button> |
|
</div> |
|
</div> |
|
|
|
<div id="requirementsAccordion" class="space-y-3"> |
|
|
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div id="adminDashboardView" class="hidden space-y-6"> |
|
<div class="flex justify-between items-center"> |
|
<h2 class="text-2xl font-bold">Admin Dashboard</h2> |
|
<button id="backToMainDashboard" class="text-blue-600 hover:text-blue-800 flex items-center"> |
|
<i class="fas fa-arrow-left mr-2"></i> Back to Main Dashboard |
|
</button> |
|
</div> |
|
|
|
<div class="bg-white p-4 rounded-lg shadow"> |
|
<div class="border-b border-gray-200"> |
|
<nav class="-mb-px flex space-x-8"> |
|
<button data-tab="users" class="tab-button border-b-2 border-blue-500 text-blue-600 whitespace-nowrap py-4 px-1 text-sm font-medium">User Management</button> |
|
<button data-tab="audit" class="tab-button border-b-2 border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 whitespace-nowrap py-4 px-1 text-sm font-medium">Audit Logs</button> |
|
<button data-tab="settings" class="tab-button border-b-2 border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 whitespace-nowrap py-4 px-1 text-sm font-medium">System Settings</button> |
|
<button data-tab="templates" class="tab-button border-b-2 border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 whitespace-nowrap py-4 px-1 text-sm font-medium">Assessment Templates</button> |
|
</nav> |
|
</div> |
|
|
|
|
|
<div id="users-tab" class="tab-content active p-4"> |
|
<div class="flex justify-between items-center mb-4"> |
|
<h3 class="text-lg font-medium">User Management</h3> |
|
<button id="addUserBtn" class="bg-blue-600 hover:bg-blue-700 text-white py-2 px-4 rounded-lg"> |
|
<i class="fas fa-plus mr-2"></i> Add User |
|
</button> |
|
</div> |
|
<div class="overflow-x-auto"> |
|
<table class="min-w-full divide-y divide-gray-200"> |
|
<thead class="bg-gray-50"> |
|
<tr> |
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Name</th> |
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Email</th> |
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Role</th> |
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Last Login</th> |
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th> |
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th> |
|
</tr> |
|
</thead> |
|
<tbody id="usersTable" class="bg-white divide-y divide-gray-200"> |
|
|
|
</tbody> |
|
</table> |
|
</div> |
|
</div> |
|
|
|
|
|
<div id="audit-tab" class="tab-content p-4"> |
|
<div class="flex justify-between items-center mb-4"> |
|
<h3 class="text-lg font-medium">Audit Logs</h3> |
|
<div class="flex space-x-2"> |
|
<input type="date" class="border rounded-lg px-3 py-2"> |
|
<input type="date" class="border rounded-lg px-3 py-2"> |
|
<button class="bg-blue-600 hover:bg-blue-700 text-white py-2 px-4 rounded-lg"> |
|
<i class="fas fa-filter mr-2"></i> Filter |
|
</button> |
|
</div> |
|
</div> |
|
<div class="overflow-x-auto"> |
|
<table class="min-w-full divide-y divide-gray-200"> |
|
<thead class="bg-gray-50"> |
|
<tr> |
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Timestamp</th> |
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">User</th> |
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Action</th> |
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Entity</th> |
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Details</th> |
|
</tr> |
|
</thead> |
|
<tbody id="auditLogsTable" class="bg-white divide-y divide-gray-200"> |
|
|
|
</tbody> |
|
</table> |
|
</div> |
|
</div> |
|
|
|
|
|
<div id="settings-tab" class="tab-content p-4"> |
|
<h3 class="text-lg font-medium mb-4">System Settings</h3> |
|
<form id="systemSettingsForm"> |
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6"> |
|
<div class="bg-gray-50 p-4 rounded-lg"> |
|
<h4 class="font-medium mb-3">General Settings</h4> |
|
<div class="mb-4"> |
|
<label class="block text-gray-700 mb-2">System Name</label> |
|
<input type="text" class="w-full px-3 py-2 border rounded-lg" value="PCI DSS Tracker"> |
|
</div> |
|
<div class="mb-4"> |
|
<label class="block text-gray-700 mb-2">Default Timezone</label> |
|
<select class="w-full px-3 py-2 border rounded-lg"> |
|
<option>UTC</option> |
|
<option selected>America/New_York</option> |
|
<option>Europe/London</option> |
|
</select> |
|
</div> |
|
<div class="mb-4"> |
|
<label class="block text-gray-700 mb-2">Date Format</label> |
|
<select class="w-full px-3 py-2 border rounded-lg"> |
|
<option>YYYY-MM-DD</option> |
|
<option selected>MM/DD/YYYY</option> |
|
<option>DD/MM/YYYY</option> |
|
</select> |
|
</div> |
|
</div> |
|
|
|
<div class="bg-gray-50 p-4 rounded-lg"> |
|
<h4 class="font-medium mb-3">Notification Settings</h4> |
|
<div class="mb-4"> |
|
<label class="flex items-center space-x-2"> |
|
<input type="checkbox" class="form-checkbox" checked> |
|
<span>Email Notifications</span> |
|
</label> |
|
</div> |
|
<div class="mb-4"> |
|
<label class="block text-gray-700 mb-2">Notification Email</label> |
|
<input type="email" class="w-full px-3 py-2 border rounded-lg" value="admin@company.com"> |
|
</div> |
|
<div class="mb-4"> |
|
<label class="block text-gray-700 mb-2">Reminder Days Before Deadline</label> |
|
<input type="number" class="w-full px-3 py-2 border rounded-lg" value="7"> |
|
</div> |
|
</div> |
|
|
|
<div class="bg-gray-50 p-4 rounded-lg"> |
|
<h4 class="font-medium mb-3">Security Settings</h4> |
|
<div class="mb-4"> |
|
<label class="flex items-center space-x-2"> |
|
<input type="checkbox" class="form-checkbox" checked> |
|
<span>Require Two-Factor Authentication</span> |
|
</label> |
|
</div> |
|
<div class="mb-4"> |
|
<label class="block text-gray-700 mb-2">Password Policy</label> |
|
<select class="w-full px-3 py-2 border rounded-lg"> |
|
<option>Basic (8 characters)</option> |
|
<option selected>Standard (12 characters, mixed case)</option> |
|
<option>Strong (16 characters, special characters)</option> |
|
</select> |
|
</div> |
|
<div class="mb-4"> |
|
<label class="block text-gray-700 mb-2">Session Timeout (minutes)</label> |
|
<input type="number" class="w-full px-3 py-2 border rounded-lg" value="30"> |
|
</div> |
|
</div> |
|
|
|
<div class="bg-gray-50 p-4 rounded-lg"> |
|
<h4 class="font-medium mb-3">Backup Settings</h4> |
|
<div class="mb-4"> |
|
<label class="flex items-center space-x-2"> |
|
<input type="checkbox" class="form-checkbox" checked> |
|
<span>Automatic Backups</span> |
|
</label> |
|
</div> |
|
<div class="mb-4"> |
|
<label class="block text-gray-700 mb-2">Backup Frequency</label> |
|
<select class="w-full px-3 py-2 border rounded-lg"> |
|
<option>Daily</option> |
|
<option selected>Weekly</option> |
|
<option>Monthly</option> |
|
</select> |
|
</div> |
|
<div class="mb-4"> |
|
<label class="block text-gray-700 mb-2">Backup Retention</label> |
|
<select class="w-full px-3 py-2 border rounded-lg"> |
|
<option>7 days</option> |
|
<option selected>30 days</option> |
|
<option>90 days</option> |
|
</select> |
|
</div> |
|
</div> |
|
</div> |
|
<div class="mt-6 flex justify-end"> |
|
<button type="button" class="px-4 py-2 border rounded-lg mr-2">Cancel</button> |
|
<button type="submit" class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">Save Settings</button> |
|
</div> |
|
</form> |
|
</div> |
|
|
|
|
|
<div id="templates-tab" class="tab-content p-4"> |
|
<div class="flex justify-between items-center mb-4"> |
|
<h3 class="text-lg font-medium">Assessment Templates</h3> |
|
<button id="addTemplateBtn" class="bg-blue-600 hover:bg-blue-700 text-white py-2 px-4 rounded-lg"> |
|
<i class="fas fa-plus mr-2"></i> Add Template |
|
</button> |
|
</div> |
|
<div class="overflow-x-auto"> |
|
<table class="min-w-full divide-y divide-gray-200"> |
|
<thead class="bg-gray-50"> |
|
<tr> |
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Template Name</th> |
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">PCI DSS Version</th> |
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Requirements</th> |
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Last Updated</th> |
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th> |
|
</tr> |
|
</thead> |
|
<tbody id="templatesTable" class="bg-white divide-y divide-gray-200"> |
|
|
|
</tbody> |
|
</table> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div id="newAssessmentModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"> |
|
<div class="bg-white rounded-lg shadow-xl w-full max-w-md"> |
|
<div class="p-4 border-b"> |
|
<h3 class="text-lg font-semibold">Create New Assessment</h3> |
|
</div> |
|
<div class="p-4"> |
|
<form id="newAssessmentForm"> |
|
<div class="mb-4"> |
|
<label class="block text-gray-700 mb-2" for="projectName">Project Name</label> |
|
<input type="text" id="projectName" class="w-full px-3 py-2 border rounded-lg" required> |
|
</div> |
|
<div class="mb-4"> |
|
<label class="block text-gray-700 mb-2" for="projectDescription">Description</label> |
|
<textarea id="projectDescription" class="w-full px-3 py-2 border rounded-lg" rows="3"></textarea> |
|
</div> |
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4"> |
|
<div> |
|
<label class="block text-gray-700 mb-2" for="startDateInput">Start Date</label> |
|
<input type="date" id="startDateInput" class="w-full px-3 py-2 border rounded-lg" required> |
|
</div> |
|
<div> |
|
<label class="block text-gray-700 mb-2" for="dueDateInput">Due Date</label> |
|
<input type="date" id="dueDateInput" class="w-full px-3 py-2 border rounded-lg" required> |
|
</div> |
|
</div> |
|
<div class="mb-4"> |
|
<label class="block text-gray-700 mb-2" for="projectManagerInput">Project Manager</label> |
|
<input type="text" id="projectManagerInput" class="w-full px-3 py-2 border rounded-lg" required> |
|
</div> |
|
<div class="mb-4"> |
|
<label class="block text-gray-700 mb-2" for="qsaInput">QSA (Qualified Security Assessor)</label> |
|
<input type="text" id="qsaInput" class="w-full px-3 py-2 border rounded-lg"> |
|
</div> |
|
</form> |
|
</div> |
|
<div class="p-4 border-t flex justify-end space-x-2"> |
|
<button id="cancelNewAssessment" class="px-4 py-2 border rounded-lg">Cancel</button> |
|
<button id="saveNewAssessment" class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">Create</button> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div id="addUserModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"> |
|
<div class="bg-white rounded-lg shadow-xl w-full max-w-md"> |
|
<div class="p-4 border-b"> |
|
<h3 class="text-lg font-semibold">Add New User</h3> |
|
</div> |
|
<div class="p-4"> |
|
<form id="addUserForm"> |
|
<div class="mb-4"> |
|
<label class="block text-gray-700 mb-2">Full Name</label> |
|
<input type="text" class="w-full px-3 py-2 border rounded-lg" required> |
|
</div> |
|
<div class="mb-4"> |
|
<label class="block text-gray-700 mb-2">Email</label> |
|
<input type="email" class="w-full px-3 py-2 border rounded-lg" required> |
|
</div> |
|
<div class="mb-4"> |
|
<label class="block text-gray-700 mb-2">Role</label> |
|
<select class="w-full px-3 py-2 border rounded-lg" required> |
|
<option value="">Select Role</option> |
|
<option value="admin">Administrator</option> |
|
<option value="manager">Compliance Manager</option> |
|
<option value="auditor">Auditor</option> |
|
<option value="user">Standard User</option> |
|
</select> |
|
</div> |
|
<div class="mb-4"> |
|
<label class="block text-gray-700 mb-2">Temporary Password</label> |
|
<input type="password" class="w-full px-3 py-2 border rounded-lg" required> |
|
</div> |
|
<div class="mb-4"> |
|
<label class="flex items-center space-x-2"> |
|
<input type="checkbox" class="form-checkbox"> |
|
<span>Require password change at first login</span> |
|
</label> |
|
</div> |
|
</form> |
|
</div> |
|
<div class="p-4 border-t flex justify-end space-x-2"> |
|
<button id="cancelAddUser" class="px-4 py-2 border rounded-lg">Cancel</button> |
|
<button id="saveAddUser" class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">Create User</button> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div id="addTemplateModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"> |
|
<div class="bg-white rounded-lg shadow-xl w-full max-w-2xl"> |
|
<div class="p-4 border-b"> |
|
<h3 class="text-lg font-semibold">Create New Assessment Template</h3> |
|
</div> |
|
<div class="p-4"> |
|
<form id="addTemplateForm"> |
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4"> |
|
<div> |
|
<label class="block text-gray-700 mb-2">Template Name</label> |
|
<input type="text" class="w-full px-3 py-2 border rounded-lg" required> |
|
</div> |
|
<div> |
|
<label class="block text-gray-700 mb-2">PCI DSS Version</label> |
|
<select class="w-full px-3 py-2 border rounded-lg" required> |
|
<option value="4.0">PCI DSS v4.0</option> |
|
<option value="3.2.1" selected>PCI DSS v3.2.1</option> |
|
<option value="3.2">PCI DSS v3.2</option> |
|
</select> |
|
</div> |
|
</div> |
|
<div class="mb-4"> |
|
<label class="block text-gray-700 mb-2">Description</label> |
|
<textarea class="w-full px-3 py-2 border rounded-lg" rows="3"></textarea> |
|
</div> |
|
<div class="mb-4"> |
|
<label class="block text-gray-700 mb-2">Requirements</label> |
|
<div class="border rounded-lg p-3 max-h-60 overflow-y-auto"> |
|
|
|
<div class="mb-2"> |
|
<label class="flex items-center space-x-2"> |
|
<input type="checkbox" class="form-checkbox" checked> |
|
<span>Install and maintain a firewall configuration to protect cardholder data</span> |
|
</label> |
|
</div> |
|
<div class="mb-2"> |
|
<label class="flex items-center space-x-2"> |
|
<input type="checkbox" class="form-checkbox" checked> |
|
<span>Do not use vendor-supplied defaults for system passwords and other security parameters</span> |
|
</label> |
|
</div> |
|
<div class="mb-2"> |
|
<label class="flex items-center space-x-2"> |
|
<input type="checkbox" class="form-checkbox" checked> |
|
<span>Protect stored cardholder data</span> |
|
</label> |
|
</div> |
|
<div class="mb-2"> |
|
<label class="flex items-center space-x-2"> |
|
<input type="checkbox" class="form-checkbox" checked> |
|
<span>Encrypt transmission of cardholder data across open, public networks</span> |
|
</label> |
|
</div> |
|
<div class="mb-2"> |
|
<label class="flex items-center space-x-2"> |
|
<input type="checkbox" class="form-checkbox" checked> |
|
<span>Protect all systems against malware and regularly update anti-virus software or programs</span> |
|
</label> |
|
</div> |
|
<div class="mb-2"> |
|
<label class="flex items-center space-x-2"> |
|
<input type="checkbox" class="form-checkbox" checked> |
|
<span>Develop and maintain secure systems and applications</span> |
|
</label> |
|
</div> |
|
</div> |
|
</div> |
|
</form> |
|
</div> |
|
<div class="p-4 border-t flex justify-end space-x-2"> |
|
<button id="cancelAddTemplate" class="px-4 py-2 border rounded-lg">Cancel</button> |
|
<button id="saveAddTemplate" class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">Create Template</button> |
|
</div> |
|
</div> |
|
</div> |
|
</main> |
|
</div> |
|
</div> |
|
|
|
<script> |
|
|
|
const pciDssRequirements = [ |
|
{ |
|
id: 'req-1', |
|
title: 'Install and maintain a firewall configuration to protect cardholder data', |
|
description: 'Establish firewall and router configuration standards that include changing default passwords, configuration reviews, and documentation of all services and protocols.', |
|
status: 'not-started', |
|
notes: '', |
|
controls: [ |
|
{ id: 'req-1-1', description: 'Establish firewall configuration standards', status: 'not-started' }, |
|
{ id: 'req-1-2', description: 'Build firewall and router configurations that restrict connections', status: 'not-started' }, |
|
{ id: 'req-1-3', description: 'Prohibit direct public access between the Internet and system components', status: 'not-started' } |
|
] |
|
}, |
|
{ |
|
id: 'req-2', |
|
title: 'Do not use vendor-supplied defaults for system passwords and other security parameters', |
|
description: 'Always change vendor-supplied defaults and remove or disable unnecessary default accounts before installing a system on the network.', |
|
status: 'in-progress', |
|
notes: 'Working with IT team to update all default credentials', |
|
controls: [ |
|
{ id: 'req-2-1', description: 'Change all vendor-supplied defaults', status: 'in-progress' }, |
|
{ id: 'req-2-2', description: 'Develop configuration standards for all system components', status: 'not-started' } |
|
] |
|
}, |
|
{ |
|
id: 'req-3', |
|
title: 'Protect stored cardholder data', |
|
description: 'Protection methods such as encryption, truncation, masking, and hashing should be used to render cardholder data unreadable.', |
|
status: 'not-started', |
|
notes: '', |
|
controls: [ |
|
{ id: 'req-3-1', description: 'Keep cardholder data storage to a minimum', status: 'not-started' }, |
|
{ id: 'req-3-2', description: 'Do not store sensitive authentication data after authorization', status: 'not-started' }, |
|
{ id: 'req-3-3', description: 'Mask PAN when displayed', status: 'not-started' }, |
|
{ id: 'req-3-4', description: 'Render PAN unreadable anywhere it is stored', status: 'not-started' } |
|
] |
|
}, |
|
{ |
|
id: 'req-4', |
|
title: 'Encrypt transmission of cardholder data across open, public networks', |
|
description: 'Strong cryptography and security protocols must be used to safeguard sensitive cardholder data during transmission over open, public networks.', |
|
status: 'completed', |
|
notes: 'All transmissions now use TLS 1.2 or higher', |
|
controls: [ |
|
{ id: 'req-4-1', description: 'Use strong cryptography and security protocols', status: 'completed' }, |
|
{ id: 'req-4-2', description: 'Never send unprotected PANs by end-user messaging technologies', status: 'completed' } |
|
] |
|
} |
|
]; |
|
|
|
|
|
let assessments = [ |
|
{ |
|
id: 'assessment-1', |
|
title: 'E-commerce Platform PCI Compliance', |
|
description: 'Annual PCI DSS assessment for our primary e-commerce platform', |
|
status: 'in-progress', |
|
progress: 35, |
|
dueDate: '2023-12-15', |
|
lastUpdated: '2023-06-10', |
|
projectManager: 'John Doe', |
|
qsa: 'Jane Smith', |
|
startDate: '2023-01-15', |
|
requirements: [...pciDssRequirements] |
|
}, |
|
{ |
|
id: 'assessment-2', |
|
title: 'Mobile App PCI Compliance', |
|
description: 'Assessment for our new mobile payment application', |
|
status: 'not-started', |
|
progress: 0, |
|
dueDate: '2023-11-30', |
|
lastUpdated: '2023-05-20', |
|
projectManager: 'Sarah Johnson', |
|
qsa: 'Michael Brown', |
|
startDate: '2023-06-01', |
|
requirements: [...pciDssRequirements] |
|
}, |
|
{ |
|
id: 'assessment-3', |
|
title: 'POS System Upgrade Compliance', |
|
description: 'Assessment for the new POS system implementation', |
|
status: 'completed', |
|
progress: 100, |
|
dueDate: '2023-03-31', |
|
lastUpdated: '2023-04-15', |
|
projectManager: 'Robert Wilson', |
|
qsa: 'Emily Davis', |
|
startDate: '2022-10-01', |
|
requirements: [...pciDssRequirements] |
|
} |
|
]; |
|
|
|
|
|
const users = [ |
|
{ id: 'user-1', name: 'Admin User', email: 'admin@company.com', role: 'admin', lastLogin: '2023-06-14 14:30', status: 'active' }, |
|
{ id: 'user-2', name: 'Compliance Manager', email: 'manager@company.com', role: 'manager', lastLogin: '2023-06-15 09:45', status: 'active' }, |
|
{ id: 'user-3', name: 'Auditor 1', email: 'auditor1@company.com', role: 'auditor', lastLogin: '2023-06-13 16:20', status: 'active' }, |
|
{ id: 'user-4', name: 'Standard User', email: 'user@company.com', role: 'user', lastLogin: '2023-06-15 11:15', status: 'active' }, |
|
{ id: 'user-5', name: 'Inactive User', email: 'inactive@company.com', role: 'user', lastLogin: '2023-05-30 10:00', status: 'inactive' } |
|
]; |
|
|
|
const auditLogs = [ |
|
{ id: 'log-1', timestamp: '2023-06-15 10:30:22', user: 'Admin User', action: 'Created', entity: 'Assessment', details: 'Created new assessment "E-commerce Platform PCI Compliance"' }, |
|
{ id: 'log-2', timestamp: '2023-06-15 09:15:10', user: 'Compliance Manager', action: 'Updated', entity: 'Requirement', details: 'Updated status for requirement "Protect stored cardholder data"' }, |
|
{ id: 'log-3', timestamp: '2023-06-14 16:45:33', user: 'Auditor 1', action: 'Exported', entity: 'Report', details: 'Exported compliance report for QSA review' }, |
|
{ id: 'log-4', timestamp: '2023-06-14 14:20:05', user: 'Admin User', action: 'Deleted', entity: 'User', details: 'Deleted inactive user account' }, |
|
{ id: 'log-5', timestamp: '2023-06-13 11:30:18', user: 'Standard User', action: 'Viewed', entity: 'Dashboard', details: 'Viewed compliance dashboard' } |
|
]; |
|
|
|
const templates = [ |
|
{ id: 'template-1', name: 'Full PCI DSS v3.2.1', version: '3.2.1', requirements: 12, lastUpdated: '2023-05-15' }, |
|
{ id: 'template-2', name: 'SAQ A', version: '3.2.1', requirements: 6, lastUpdated: '2023-04-20' }, |
|
{ id: 'template-3', name: 'SAQ D', version: '3.2.1', requirements: 12, lastUpdated: '2023-03-10' }, |
|
{ id: 'template-4', name: 'PCI DSS v4.0 Draft', version: '4.0', requirements: 12, lastUpdated: '2023-06-01' } |
|
]; |
|
|
|
|
|
const projectList = document.getElementById('projectList'); |
|
const recentAssessmentsTable = document.getElementById('recentAssessmentsTable'); |
|
const dashboardView = document.getElementById('dashboardView'); |
|
const assessmentDetailView = document.getElementById('assessmentDetailView'); |
|
const adminDashboardView = document.getElementById('adminDashboardView'); |
|
const requirementsAccordion = document.getElementById('requirementsAccordion'); |
|
const newAssessmentModal = document.getElementById('newAssessmentModal'); |
|
const newAssessmentForm = document.getElementById('newAssessmentForm'); |
|
const newAssessmentBtn = document.getElementById('newAssessmentBtn'); |
|
const saveNewAssessment = document.getElementById('saveNewAssessment'); |
|
const cancelNewAssessment = document.getElementById('cancelNewAssessment'); |
|
const backToDashboard = document.getElementById('backToDashboard'); |
|
const backToMainDashboard = document.getElementById('backToMainDashboard'); |
|
const expandAll = document.getElementById('expandAll'); |
|
const collapseAll = document.getElementById('collapseAll'); |
|
const sidebarToggle = document.getElementById('sidebarToggle'); |
|
const mobileSidebarToggle = document.getElementById('mobileSidebarToggle'); |
|
const sidebar = document.querySelector('.sidebar'); |
|
const adminDashboardBtn = document.getElementById('adminDashboardBtn'); |
|
const tabButtons = document.querySelectorAll('.tab-button'); |
|
const tabContents = document.querySelectorAll('.tab-content'); |
|
const usersTable = document.getElementById('usersTable'); |
|
const auditLogsTable = document.getElementById('auditLogsTable'); |
|
const templatesTable = document.getElementById('templatesTable'); |
|
const addUserBtn = document.getElementById('addUserBtn'); |
|
const addUserModal = document.getElementById('addUserModal'); |
|
const cancelAddUser = document.getElementById('cancelAddUser'); |
|
const saveAddUser = document.getElementById('saveAddUser'); |
|
const addTemplateBtn = document.getElementById('addTemplateBtn'); |
|
const addTemplateModal = document.getElementById('addTemplateModal'); |
|
const cancelAddTemplate = document.getElementById('cancelAddTemplate'); |
|
const saveAddTemplate = document.getElementById('saveAddTemplate'); |
|
|
|
|
|
let currentAssessment = null; |
|
let currentTab = 'users'; |
|
|
|
|
|
function init() { |
|
renderProjectList(); |
|
renderRecentAssessments(); |
|
updateDashboardMetrics(); |
|
renderAdminUsers(); |
|
renderAuditLogs(); |
|
renderTemplates(); |
|
|
|
|
|
newAssessmentBtn.addEventListener('click', showNewAssessmentModal); |
|
saveNewAssessment.addEventListener('click', saveAssessment); |
|
cancelNewAssessment.addEventListener('click', hideNewAssessmentModal); |
|
backToDashboard.addEventListener('click', showDashboardView); |
|
backToMainDashboard.addEventListener('click', showDashboardView); |
|
expandAll.addEventListener('click', () => toggleAllRequirements('expand')); |
|
collapseAll.addEventListener('click', () => toggleAllRequirements('collapse')); |
|
sidebarToggle.addEventListener('click', toggleSidebar); |
|
mobileSidebarToggle.addEventListener('click', toggleSidebar); |
|
adminDashboardBtn.addEventListener('click', showAdminDashboard); |
|
|
|
|
|
tabButtons.forEach(button => { |
|
button.addEventListener('click', () => { |
|
const tabId = button.dataset.tab; |
|
switchTab(tabId); |
|
}); |
|
}); |
|
|
|
|
|
addUserBtn.addEventListener('click', () => addUserModal.classList.remove('hidden')); |
|
cancelAddUser.addEventListener('click', () => addUserModal.classList.add('hidden')); |
|
saveAddUser.addEventListener('click', saveUser); |
|
addTemplateBtn.addEventListener('click', () => addTemplateModal.classList.remove('hidden')); |
|
cancelAddTemplate.addEventListener('click', () => addTemplateModal.classList.add('hidden')); |
|
saveAddTemplate.addEventListener('click', saveTemplate); |
|
|
|
|
|
[newAssessmentModal, addUserModal, addTemplateModal].forEach(modal => { |
|
modal.addEventListener('click', (e) => { |
|
if (e.target === modal) { |
|
modal.classList.add('hidden'); |
|
} |
|
}); |
|
}); |
|
} |
|
|
|
|
|
function renderProjectList() { |
|
projectList.innerHTML = ''; |
|
assessments.forEach(assessment => { |
|
const listItem = document.createElement('li'); |
|
listItem.className = 'cursor-pointer hover:bg-blue-700 px-2 py-1 rounded'; |
|
listItem.innerHTML = ` |
|
<div class="flex items-center justify-between"> |
|
<span>${assessment.title}</span> |
|
<span class="text-xs ${getStatusColor(assessment.status)}">${getStatusText(assessment.status)}</span> |
|
</div> |
|
`; |
|
listItem.addEventListener('click', () => showAssessmentDetail(assessment.id)); |
|
projectList.appendChild(listItem); |
|
}); |
|
} |
|
|
|
|
|
function renderRecentAssessments() { |
|
recentAssessmentsTable.innerHTML = ''; |
|
assessments.forEach(assessment => { |
|
const row = document.createElement('tr'); |
|
row.className = 'hover:bg-gray-50 cursor-pointer'; |
|
row.innerHTML = ` |
|
<td class="px-6 py-4 whitespace-nowrap"> |
|
<div class="font-medium">${assessment.title}</div> |
|
<div class="text-sm text-gray-500">${assessment.description}</div> |
|
</td> |
|
<td class="px-6 py-4 whitespace-nowrap"> |
|
<span class="px-2 py-1 text-xs rounded-full ${getStatusColor(assessment.status)}">${getStatusText(assessment.status)}</span> |
|
</td> |
|
<td class="px-6 py-4 whitespace-nowrap"> |
|
<div class="flex items-center"> |
|
<div class="w-full bg-gray-200 rounded-full h-2.5"> |
|
<div class="bg-blue-600 h-2.5 rounded-full progress-bar" style="width: ${assessment.progress}%"></div> |
|
</div> |
|
<span class="ml-2 text-sm font-medium text-gray-700">${assessment.progress}%</span> |
|
</div> |
|
</td> |
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> |
|
${assessment.dueDate} |
|
</td> |
|
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium"> |
|
<button class="text-blue-600 hover:text-blue-900 mr-3">Edit</button> |
|
<button class="text-red-600 hover:text-red-900">Delete</button> |
|
</td> |
|
`; |
|
row.addEventListener('click', () => showAssessmentDetail(assessment.id)); |
|
recentAssessmentsTable.appendChild(row); |
|
}); |
|
} |
|
|
|
|
|
function renderAdminUsers() { |
|
usersTable.innerHTML = ''; |
|
users.forEach(user => { |
|
const row = document.createElement('tr'); |
|
row.className = 'hover:bg-gray-50'; |
|
row.innerHTML = ` |
|
<td class="px-6 py-4 whitespace-nowrap"> |
|
<div class="flex items-center"> |
|
<div class="flex-shrink-0 h-10 w-10 rounded-full bg-blue-100 flex items-center justify-center text-blue-600"> |
|
<i class="fas fa-user"></i> |
|
</div> |
|
<div class="ml-4"> |
|
<div class="font-medium">${user.name}</div> |
|
<div class="text-sm text-gray-500">${user.role.charAt(0).toUpperCase() + user.role.slice(1)}</div> |
|
</div> |
|
</div> |
|
</td> |
|
<td class="px-6 py-4 whitespace-nowrap"> |
|
<div class="text-sm">${user.email}</div> |
|
</td> |
|
<td class="px-6 py-4 whitespace-nowrap"> |
|
<span class="px-2 py-1 text-xs rounded-full ${user.role === 'admin' ? 'bg-purple-100 text-purple-800' : 'bg-blue-100 text-blue-800'}"> |
|
${user.role.charAt(0).toUpperCase() + user.role.slice(1)} |
|
</span> |
|
</td> |
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> |
|
${user.lastLogin} |
|
</td> |
|
<td class="px-6 py-4 whitespace-nowrap"> |
|
<span class="px-2 py-1 text-xs rounded-full ${user.status === 'active' ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-800'}"> |
|
${user.status.charAt(0).toUpperCase() + user.status.slice(1)} |
|
</span> |
|
</td> |
|
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium"> |
|
<button class="text-blue-600 hover:text-blue-900 mr-3">Edit</button> |
|
<button class="text-red-600 hover:text-red-900">${user.status === 'active' ? 'Deactivate' : 'Activate'}</button> |
|
</td> |
|
`; |
|
usersTable.appendChild(row); |
|
}); |
|
} |
|
|
|
|
|
function renderAuditLogs() { |
|
auditLogsTable.innerHTML = ''; |
|
auditLogs.forEach(log => { |
|
const row = document.createElement('tr'); |
|
row.className = 'hover:bg-gray-50'; |
|
row.innerHTML = ` |
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> |
|
${log.timestamp} |
|
</td> |
|
<td class="px-6 py-4 whitespace-nowrap"> |
|
<div class="text-sm font-medium">${log.user}</div> |
|
</td> |
|
<td class="px-6 py-4 whitespace-nowrap"> |
|
<span class="px-2 py-1 text-xs rounded-full ${getActionColor(log.action)}"> |
|
${log.action} |
|
</span> |
|
</td> |
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> |
|
${log.entity} |
|
</td> |
|
<td class="px-6 py-4 text-sm text-gray-500"> |
|
${log.details} |
|
</td> |
|
`; |
|
auditLogsTable.appendChild(row); |
|
}); |
|
} |
|
|
|
|
|
function renderTemplates() { |
|
templatesTable.innerHTML = ''; |
|
templates.forEach(template => { |
|
const row = document.createElement('tr'); |
|
row.className = 'hover:bg-gray-50'; |
|
row.innerHTML = ` |
|
<td class="px-6 py-4 whitespace-nowrap"> |
|
<div class="font-medium">${template.name}</div> |
|
</td> |
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> |
|
v${template.version} |
|
</td> |
|
<td class="px-6 py-4 whitespace-nowrap"> |
|
<span class="px-2 py-1 text-xs rounded-full bg-blue-100 text-blue-800"> |
|
${template.requirements} requirements |
|
</span> |
|
</td> |
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> |
|
${template.lastUpdated} |
|
</td> |
|
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium"> |
|
<button class="text-blue-600 hover:text-blue-900 mr-3">Edit</button> |
|
<button class="text-red-600 hover:text-red-900">Delete</button> |
|
</td> |
|
`; |
|
templatesTable.appendChild(row); |
|
}); |
|
} |
|
|
|
|
|
function showAssessmentDetail(assessmentId) { |
|
currentAssessment = assessments.find(a => a.id === assessmentId); |
|
if (!currentAssessment) return; |
|
|
|
|
|
document.getElementById('assessmentTitle').textContent = currentAssessment.title; |
|
document.getElementById('assessmentStatus').textContent = getStatusText(currentAssessment.status); |
|
document.getElementById('assessmentStatus').className = `px-3 py-1 rounded-full text-xs font-semibold ${getStatusColor(currentAssessment.status)}`; |
|
document.getElementById('assessmentDueDate').textContent = `Due: ${currentAssessment.dueDate}`; |
|
document.getElementById('lastUpdated').textContent = currentAssessment.lastUpdated; |
|
document.getElementById('assessmentCompliance').textContent = `${currentAssessment.progress}%`; |
|
document.getElementById('assessmentComplianceBar').style.width = `${currentAssessment.progress}%`; |
|
document.getElementById('projectManager').textContent = currentAssessment.projectManager; |
|
document.getElementById('qsaName').textContent = currentAssessment.qsa; |
|
document.getElementById('startDate').textContent = currentAssessment.startDate; |
|
document.getElementById('completionTarget').textContent = currentAssessment.dueDate; |
|
|
|
|
|
renderRequirements(currentAssessment.requirements); |
|
|
|
|
|
dashboardView.classList.add('hidden'); |
|
adminDashboardView.classList.add('hidden'); |
|
assessmentDetailView.classList.remove('hidden'); |
|
document.getElementById('currentProjectTitle').textContent = currentAssessment.title; |
|
} |
|
|
|
|
|
function renderRequirements(requirements) { |
|
requirementsAccordion.innerHTML = ''; |
|
requirements.forEach(req => { |
|
const requirementItem = document.createElement('div'); |
|
requirementItem.className = 'requirement-item bg-white border rounded-lg overflow-hidden transition-all'; |
|
requirementItem.innerHTML = ` |
|
<div class="requirement-header flex justify-between items-center p-4 cursor-pointer border-b"> |
|
<div class="flex items-center"> |
|
<span class="mr-3 text-gray-500">${req.id}</span> |
|
<h4 class="font-medium">${req.title}</h4> |
|
</div> |
|
<div class="flex items-center"> |
|
<span class="px-2 py-1 text-xs rounded-full ${getStatusColor(req.status)} mr-3">${getStatusText(req.status)}</span> |
|
<i class="fas fa-chevron-down transition-transform"></i> |
|
</div> |
|
</div> |
|
<div class="requirement-content hidden p-4 bg-gray-50"> |
|
<p class="text-gray-700 mb-4">${req.description}</p> |
|
${req.notes ? `<div class="mb-4 p-3 bg-yellow-50 border-l-4 border-yellow-400"> |
|
<p class="text-sm text-yellow-700"><strong>Notes:</strong> ${req.notes}</p> |
|
</div>` : ''} |
|
<div class="controls-list space-y-3"> |
|
${req.controls.map(control => ` |
|
<div class="control-item flex items-center justify-between p-3 bg-white border rounded"> |
|
<div> |
|
<p class="text-sm font-medium">${control.id}: ${control.description}</p> |
|
</div> |
|
<select class="status-select text-xs border rounded p-1" data-req-id="${req.id}" data-control-id="${control.id}"> |
|
<option value="not-started" ${control.status === 'not-started' ? 'selected' : ''}>Not Started</option> |
|
<option value="in-progress" ${control.status === 'in-progress' ? 'selected' : ''}>In Progress</option> |
|
<option value="completed" ${control.status === 'completed' ? 'selected' : ''}>Completed</option> |
|
</select> |
|
</div> |
|
`).join('')} |
|
</div> |
|
<div class="mt-4"> |
|
<label class="block text-sm font-medium text-gray-700 mb-1">Add Notes</label> |
|
<textarea class="w-full px-3 py-2 border rounded-lg notes-textarea" data-req-id="${req.id}" placeholder="Add notes about this requirement...">${req.notes || ''}</textarea> |
|
</div> |
|
</div> |
|
`; |
|
|
|
const header = requirementItem.querySelector('.requirement-header'); |
|
const content = requirementItem.querySelector('.requirement-content'); |
|
const chevron = requirementItem.querySelector('.fa-chevron-down'); |
|
|
|
header.addEventListener('click', () => { |
|
content.classList.toggle('hidden'); |
|
chevron.classList.toggle('rotate-180'); |
|
}); |
|
|
|
|
|
const statusSelects = requirementItem.querySelectorAll('.status-select'); |
|
statusSelects.forEach(select => { |
|
select.addEventListener('change', (e) => { |
|
const reqId = e.target.dataset.reqId; |
|
const controlId = e.target.dataset.controlId; |
|
const newStatus = e.target.value; |
|
|
|
|
|
const requirement = currentAssessment.requirements.find(r => r.id === reqId); |
|
if (requirement) { |
|
const control = requirement.controls.find(c => c.id === controlId); |
|
if (control) { |
|
control.status = newStatus; |
|
} |
|
} |
|
|
|
|
|
updateRequirementStatus(reqId); |
|
updateAssessmentProgress(); |
|
}); |
|
}); |
|
|
|
|
|
const notesTextarea = requirementItem.querySelector('.notes-textarea'); |
|
notesTextarea.addEventListener('change', (e) => { |
|
const reqId = e.target.dataset.reqId; |
|
const notes = e.target.value; |
|
|
|
const requirement = currentAssessment.requirements.find(r => r.id === reqId); |
|
if (requirement) { |
|
requirement.notes = notes; |
|
} |
|
}); |
|
|
|
requirementsAccordion.appendChild(requirementItem); |
|
}); |
|
} |
|
|
|
|
|
function updateRequirementStatus(reqId) { |
|
const requirement = currentAssessment.requirements.find(r => r.id === reqId); |
|
if (!requirement) return; |
|
|
|
const controls = requirement.controls; |
|
if (controls.every(c => c.status === 'completed')) { |
|
requirement.status = 'completed'; |
|
} else if (controls.some(c => c.status === 'in-progress' || c.status === 'completed')) { |
|
requirement.status = 'in-progress'; |
|
} else { |
|
requirement.status = 'not-started'; |
|
} |
|
|
|
|
|
const requirementItem = document.querySelector(`.requirement-item [data-req-id="${reqId}"]`); |
|
if (requirementItem) { |
|
const statusBadge = requirementItem.closest('.requirement-item').querySelector('.requirement-header span'); |
|
statusBadge.textContent = getStatusText(requirement.status); |
|
statusBadge.className = `px-2 py-1 text-xs rounded-full ${getStatusColor(requirement.status)} mr-3`; |
|
} |
|
} |
|
|
|
|
|
function updateAssessmentProgress() { |
|
if (!currentAssessment) return; |
|
|
|
const requirements = currentAssessment.requirements; |
|
const totalControls = requirements.reduce((sum, req) => sum + req.controls.length, 0); |
|
const completedControls = requirements.reduce((sum, req) => { |
|
return sum + req.controls.filter(c => c.status === 'completed').length; |
|
}, 0); |
|
|
|
const progress = Math.round((completedControls / totalControls) * 100); |
|
currentAssessment.progress = progress; |
|
|
|
|
|
document.getElementById('assessmentCompliance').textContent = `${progress}%`; |
|
document.getElementById('assessmentComplianceBar').style.width = `${progress}%`; |
|
|
|
|
|
const assessmentIndex = assessments.findIndex(a => a.id === currentAssessment.id); |
|
if (assessmentIndex !== -1) { |
|
assessments[assessmentIndex] = currentAssessment; |
|
renderProjectList(); |
|
renderRecentAssessments(); |
|
updateDashboardMetrics(); |
|
} |
|
} |
|
|
|
|
|
function showDashboardView() { |
|
dashboardView.classList.remove('hidden'); |
|
assessmentDetailView.classList.add('hidden'); |
|
adminDashboardView.classList.add('hidden'); |
|
document.getElementById('currentProjectTitle').textContent = 'Dashboard'; |
|
} |
|
|
|
|
|
function showAdminDashboard() { |
|
dashboardView.classList.add('hidden'); |
|
assessmentDetailView.classList.add('hidden'); |
|
adminDashboardView.classList.remove('hidden'); |
|
document.getElementById('currentProjectTitle').textContent = 'Admin Dashboard'; |
|
switchTab(currentTab); |
|
} |
|
|
|
|
|
function switchTab(tabId) { |
|
currentTab = tabId; |
|
|
|
|
|
tabButtons.forEach(button => { |
|
if (button.dataset.tab === tabId) { |
|
button.classList.add('border-blue-500', 'text-blue-600'); |
|
button.classList.remove('border-transparent', 'text-gray-500', 'hover:text-gray-700', 'hover:border-gray-300'); |
|
} else { |
|
button.classList.remove('border-blue-500', 'text-blue-600'); |
|
button.classList.add('border-transparent', 'text-gray-500', 'hover:text-gray-700', 'hover:border-gray-300'); |
|
} |
|
}); |
|
|
|
|
|
tabContents.forEach(content => { |
|
if (content.id === `${tabId}-tab`) { |
|
content.classList.add('active'); |
|
} else { |
|
content.classList.remove('active'); |
|
} |
|
}); |
|
} |
|
|
|
|
|
function showNewAssessmentModal() { |
|
newAssessmentModal.classList.remove('hidden'); |
|
} |
|
|
|
|
|
function hideNewAssessmentModal() { |
|
newAssessmentModal.classList.add('hidden'); |
|
newAssessmentForm.reset(); |
|
} |
|
|
|
|
|
function saveAssessment() { |
|
const projectName = document.getElementById('projectName').value; |
|
const projectDescription = document.getElementById('projectDescription').value; |
|
const startDate = document.getElementById('startDateInput').value; |
|
const dueDate = document.getElementById('dueDateInput').value; |
|
const projectManager = document.getElementById('projectManagerInput').value; |
|
const qsa = document.getElementById('qsaInput').value; |
|
|
|
if (!projectName || !startDate || !dueDate || !projectManager) { |
|
alert('Please fill in all required fields'); |
|
return; |
|
} |
|
|
|
const newAssessment = { |
|
id: `assessment-${Date.now()}`, |
|
title: projectName, |
|
description: projectDescription, |
|
status: 'not-started', |
|
progress: 0, |
|
dueDate: dueDate, |
|
lastUpdated: new Date().toISOString().split('T')[0], |
|
projectManager: projectManager, |
|
qsa: qsa, |
|
startDate: startDate, |
|
requirements: [...pciDssRequirements] |
|
}; |
|
|
|
assessments.push(newAssessment); |
|
renderProjectList(); |
|
renderRecentAssessments(); |
|
updateDashboardMetrics(); |
|
hideNewAssessmentModal(); |
|
} |
|
|
|
|
|
function saveUser() { |
|
|
|
alert('User created successfully!'); |
|
addUserModal.classList.add('hidden'); |
|
|
|
renderAdminUsers(); |
|
} |
|
|
|
|
|
function saveTemplate() { |
|
|
|
alert('Template created successfully!'); |
|
addTemplateModal.classList.add('hidden'); |
|
|
|
renderTemplates(); |
|
} |
|
|
|
|
|
function toggleAllRequirements(action) { |
|
const contents = document.querySelectorAll('.requirement-content'); |
|
const chevrons = document.querySelectorAll('.requirement-header .fa-chevron-down'); |
|
|
|
contents.forEach(content => { |
|
if (action === 'expand') { |
|
content.classList.remove('hidden'); |
|
} else { |
|
content.classList.add('hidden'); |
|
} |
|
}); |
|
|
|
chevrons.forEach(chevron => { |
|
if (action === 'expand') { |
|
chevron.classList.add('rotate-180'); |
|
} else { |
|
chevron.classList.remove('rotate-180'); |
|
} |
|
}); |
|
} |
|
|
|
|
|
function toggleSidebar() { |
|
sidebar.classList.toggle('open'); |
|
} |
|
|
|
|
|
function updateDashboardMetrics() { |
|
const activeAssessments = assessments.filter(a => a.status !== 'completed').length; |
|
const completedAssessments = assessments.filter(a => a.status === 'completed'); |
|
const totalProgress = assessments.reduce((sum, a) => sum + a.progress, 0) / assessments.length || 0; |
|
const upcomingDeadlines = assessments.filter(a => { |
|
const dueDate = new Date(a.dueDate); |
|
const today = new Date(); |
|
const diffTime = dueDate - today; |
|
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); |
|
return diffDays <= 30 && diffDays >= 0 && a.status !== 'completed'; |
|
}).length; |
|
|
|
document.getElementById('activeAssessmentsCount').textContent = activeAssessments; |
|
document.getElementById('complianceScore').textContent = `${Math.round(totalProgress)}%`; |
|
document.getElementById('complianceBar').style.width = `${Math.round(totalProgress)}%`; |
|
document.getElementById('upcomingDeadlines').textContent = upcomingDeadlines; |
|
} |
|
|
|
|
|
function getStatusText(status) { |
|
switch (status) { |
|
case 'not-started': return 'Not Started'; |
|
case 'in-progress': return 'In Progress'; |
|
case 'completed': return 'Completed'; |
|
default: return status; |
|
} |
|
} |
|
|
|
function getStatusColor(status) { |
|
switch (status) { |
|
case 'not-started': return 'bg-gray-100 text-gray-800'; |
|
case 'in-progress': return 'bg-blue-100 text-blue-800'; |
|
case 'completed': return 'bg-green-100 text-green-800'; |
|
default: return 'bg-gray-100 text-gray-800'; |
|
} |
|
} |
|
|
|
function getActionColor(action) { |
|
switch (action.toLowerCase()) { |
|
case 'created': return 'bg-green-100 text-green-800'; |
|
case 'updated': return 'bg-blue-100 text-blue-800'; |
|
case 'deleted': return 'bg-red-100 text-red-800'; |
|
case 'viewed': return 'bg-gray-100 text-gray-800'; |
|
default: return 'bg-gray-100 text-gray-800'; |
|
} |
|
} |
|
|
|
|
|
document.addEventListener('DOMContentLoaded', init); |
|
</script> |
|
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=vjackl/my-first-pci-dss-vibe-code-project" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
|
</html> |