Spaces:
Running
Running
| <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/pci-dss-assessment-tool" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |