vjackl's picture
Add 2 files
2ac0351 verified
<!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">
<!-- Sidebar -->
<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">
<!-- Projects will be added here dynamically -->
</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>
<!-- Main Content -->
<div class="flex-1 flex flex-col overflow-hidden">
<!-- Top Navigation -->
<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 Content Area -->
<main class="flex-1 overflow-y-auto p-4 bg-gray-50">
<!-- Dashboard Overview -->
<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">
<!-- Assessments will be added here dynamically -->
</tbody>
</table>
</div>
</div>
</div>
<!-- Assessment Detail View (hidden by default) -->
<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">
<!-- Requirements will be added here dynamically -->
</div>
</div>
</div>
</div>
<!-- Admin Dashboard View -->
<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>
<!-- Users Tab Content -->
<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">
<!-- Users will be added here dynamically -->
</tbody>
</table>
</div>
</div>
<!-- Audit Logs Tab Content -->
<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">
<!-- Audit logs will be added here dynamically -->
</tbody>
</table>
</div>
</div>
<!-- System Settings Tab Content -->
<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>
<!-- Assessment Templates Tab Content -->
<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">
<!-- Templates will be added here dynamically -->
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- New Assessment Modal -->
<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>
<!-- Add User Modal -->
<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>
<!-- Add Template Modal -->
<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">
<!-- Sample requirements checklist -->
<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>
// Sample data for PCI DSS requirements
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' }
]
}
];
// Sample assessments data
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]
}
];
// Sample admin data
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' }
];
// DOM elements
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');
// Current state
let currentAssessment = null;
let currentTab = 'users';
// Initialize the app
function init() {
renderProjectList();
renderRecentAssessments();
updateDashboardMetrics();
renderAdminUsers();
renderAuditLogs();
renderTemplates();
// Event listeners
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);
// Tab switching
tabButtons.forEach(button => {
button.addEventListener('click', () => {
const tabId = button.dataset.tab;
switchTab(tabId);
});
});
// Admin modals
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);
// Close modals when clicking outside
[newAssessmentModal, addUserModal, addTemplateModal].forEach(modal => {
modal.addEventListener('click', (e) => {
if (e.target === modal) {
modal.classList.add('hidden');
}
});
});
}
// Render project list in sidebar
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);
});
}
// Render recent assessments table
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);
});
}
// Render admin users table
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);
});
}
// Render audit logs table
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);
});
}
// Render templates table
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);
});
}
// Show assessment detail view
function showAssessmentDetail(assessmentId) {
currentAssessment = assessments.find(a => a.id === assessmentId);
if (!currentAssessment) return;
// Update the assessment detail view
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;
// Render requirements
renderRequirements(currentAssessment.requirements);
// Switch views
dashboardView.classList.add('hidden');
adminDashboardView.classList.add('hidden');
assessmentDetailView.classList.remove('hidden');
document.getElementById('currentProjectTitle').textContent = currentAssessment.title;
}
// Render requirements accordion
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');
});
// Add event listeners for status changes
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;
// Update the control status in the current assessment
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;
}
}
// Update the requirement status based on controls
updateRequirementStatus(reqId);
updateAssessmentProgress();
});
});
// Add event listener for notes
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);
});
}
// Update requirement status based on its controls
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';
}
// Update the UI
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`;
}
}
// Update assessment progress based on requirements
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;
// Update the UI
document.getElementById('assessmentCompliance').textContent = `${progress}%`;
document.getElementById('assessmentComplianceBar').style.width = `${progress}%`;
// Update the assessment in the assessments array
const assessmentIndex = assessments.findIndex(a => a.id === currentAssessment.id);
if (assessmentIndex !== -1) {
assessments[assessmentIndex] = currentAssessment;
renderProjectList();
renderRecentAssessments();
updateDashboardMetrics();
}
}
// Show dashboard view
function showDashboardView() {
dashboardView.classList.remove('hidden');
assessmentDetailView.classList.add('hidden');
adminDashboardView.classList.add('hidden');
document.getElementById('currentProjectTitle').textContent = 'Dashboard';
}
// Show admin dashboard view
function showAdminDashboard() {
dashboardView.classList.add('hidden');
assessmentDetailView.classList.add('hidden');
adminDashboardView.classList.remove('hidden');
document.getElementById('currentProjectTitle').textContent = 'Admin Dashboard';
switchTab(currentTab);
}
// Switch between admin tabs
function switchTab(tabId) {
currentTab = tabId;
// Update tab buttons
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');
}
});
// Update tab contents
tabContents.forEach(content => {
if (content.id === `${tabId}-tab`) {
content.classList.add('active');
} else {
content.classList.remove('active');
}
});
}
// Show new assessment modal
function showNewAssessmentModal() {
newAssessmentModal.classList.remove('hidden');
}
// Hide new assessment modal
function hideNewAssessmentModal() {
newAssessmentModal.classList.add('hidden');
newAssessmentForm.reset();
}
// Save new assessment
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();
}
// Save new user
function saveUser() {
// In a real app, you would validate and save the user data here
alert('User created successfully!');
addUserModal.classList.add('hidden');
// Refresh users list
renderAdminUsers();
}
// Save new template
function saveTemplate() {
// In a real app, you would validate and save the template data here
alert('Template created successfully!');
addTemplateModal.classList.add('hidden');
// Refresh templates list
renderTemplates();
}
// Toggle all requirements
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');
}
});
}
// Toggle sidebar on mobile
function toggleSidebar() {
sidebar.classList.toggle('open');
}
// Update dashboard metrics
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;
}
// Helper functions
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';
}
}
// Initialize the app
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>