LukasBe commited on
Commit
9d86da9
·
verified ·
1 Parent(s): e887eb3

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +1404 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Complyhub
3
- emoji: 🏆
4
- colorFrom: green
5
- colorTo: yellow
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: complyhub
3
+ emoji: 🐳
4
+ colorFrom: pink
5
+ colorTo: pink
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,1404 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>ComplyHub | SMB Compliance Management</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <style>
10
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
11
+
12
+ body {
13
+ font-family: 'Inter', sans-serif;
14
+ background-color: #f8fafc;
15
+ }
16
+
17
+ .sidebar {
18
+ transition: all 0.3s ease;
19
+ }
20
+
21
+ .compliance-card {
22
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
23
+ }
24
+
25
+ .compliance-card:hover {
26
+ transform: translateY(-2px);
27
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
28
+ }
29
+
30
+ .progress-ring__circle {
31
+ transition: stroke-dashoffset 0.35s;
32
+ transform: rotate(-90deg);
33
+ transform-origin: 50% 50%;
34
+ }
35
+
36
+ .animate-pulse {
37
+ animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
38
+ }
39
+
40
+ @keyframes pulse {
41
+ 0%, 100% {
42
+ opacity: 1;
43
+ }
44
+ 50% {
45
+ opacity: 0.5;
46
+ }
47
+ }
48
+
49
+ .modal {
50
+ transition: opacity 0.3s ease, transform 0.3s ease;
51
+ }
52
+
53
+ .modal-overlay {
54
+ background-color: rgba(0, 0, 0, 0.5);
55
+ }
56
+ </style>
57
+ </head>
58
+ <body class="bg-gray-50">
59
+ <div class="flex h-screen overflow-hidden">
60
+ <!-- Sidebar -->
61
+ <div class="sidebar bg-indigo-700 text-white w-64 flex-shrink-0 hidden md:flex md:flex-col">
62
+ <div class="flex items-center justify-center h-16 px-4 border-b border-indigo-600">
63
+ <div class="flex items-center">
64
+ <i class="fas fa-shield-alt text-2xl text-indigo-200 mr-2"></i>
65
+ <span class="text-xl font-semibold">ComplyHub</span>
66
+ </div>
67
+ </div>
68
+ <div class="flex-grow overflow-y-auto">
69
+ <nav class="px-4 py-6">
70
+ <div class="mb-8">
71
+ <h3 class="text-xs uppercase font-semibold text-indigo-300 tracking-wider mb-4">Dashboard</h3>
72
+ <ul>
73
+ <li class="mb-2">
74
+ <a href="#" class="flex items-center px-3 py-2 rounded-lg bg-indigo-800 text-white">
75
+ <i class="fas fa-tachometer-alt mr-3 text-indigo-300"></i>
76
+ Overview
77
+ </a>
78
+ </li>
79
+ <li class="mb-2">
80
+ <a href="#" class="flex items-center px-3 py-2 rounded-lg text-indigo-200 hover:bg-indigo-800 hover:text-white">
81
+ <i class="fas fa-calendar-check mr-3"></i>
82
+ Compliance Calendar
83
+ </a>
84
+ </li>
85
+ <li class="mb-2">
86
+ <a href="#" class="flex items-center px-3 py-2 rounded-lg text-indigo-200 hover:bg-indigo-800 hover:text-white">
87
+ <i class="fas fa-tasks mr-3"></i>
88
+ Tasks
89
+ </a>
90
+ </li>
91
+ </ul>
92
+ </div>
93
+
94
+ <div class="mb-8">
95
+ <h3 class="text-xs uppercase font-semibold text-indigo-300 tracking-wider mb-4">Compliance</h3>
96
+ <ul>
97
+ <li class="mb-2">
98
+ <a href="#" class="flex items-center px-3 py-2 rounded-lg text-indigo-200 hover:bg-indigo-800 hover:text-white">
99
+ <i class="fas fa-file-invoice mr-3"></i>
100
+ Policies
101
+ </a>
102
+ </li>
103
+ <li class="mb-2">
104
+ <a href="#" class="flex items-center px-3 py-2 rounded-lg text-indigo-200 hover:bg-indigo-800 hover:text-white">
105
+ <i class="fas fa-file-contract mr-3"></i>
106
+ Documents
107
+ </a>
108
+ </li>
109
+ <li class="mb-2">
110
+ <a href="#" class="flex items-center px-3 py-2 rounded-lg text-indigo-200 hover:bg-indigo-800 hover:text-white">
111
+ <i class="fas fa-clipboard-list mr-3"></i>
112
+ Frameworks
113
+ </a>
114
+ </li>
115
+ </ul>
116
+ </div>
117
+
118
+ <div class="mb-8">
119
+ <h3 class="text-xs uppercase font-semibold text-indigo-300 tracking-wider mb-4">Risk Management</h3>
120
+ <ul>
121
+ <li class="mb-2">
122
+ <a href="#" class="flex items-center px-3 py-2 rounded-lg text-indigo-200 hover:bg-indigo-800 hover:text-white">
123
+ <i class="fas fa-exclamation-triangle mr-3"></i>
124
+ Risk Assessment
125
+ </a>
126
+ </li>
127
+ <li class="mb-2">
128
+ <a href="#" class="flex items-center px-3 py-2 rounded-lg text-indigo-200 hover:bg-indigo-800 hover:text-white">
129
+ <i class="fas fa-bug mr-3"></i>
130
+ Audit Logs
131
+ </a>
132
+ </li>
133
+ </ul>
134
+ </div>
135
+
136
+ <div>
137
+ <h3 class="text-xs uppercase font-semibold text-indigo-300 tracking-wider mb-4">Settings</h3>
138
+ <ul>
139
+ <li class="mb-2">
140
+ <a href="#" class="flex items-center px-3 py-2 rounded-lg text-indigo-200 hover:bg-indigo-800 hover:text-white">
141
+ <i class="fas fa-users-cog mr-3"></i>
142
+ Team
143
+ </a>
144
+ </li>
145
+ <li class="mb-2">
146
+ <a href="#" class="flex items-center px-3 py-2 rounded-lg text-indigo-200 hover:bg-indigo-800 hover:text-white">
147
+ <i class="fas fa-cog mr-3"></i>
148
+ Settings
149
+ </a>
150
+ </li>
151
+ </ul>
152
+ </div>
153
+ </nav>
154
+ </div>
155
+ <div class="px-4 py-4 border-t border-indigo-600">
156
+ <div class="flex items-center">
157
+ <img class="w-8 h-8 rounded-full" src="https://randomuser.me/api/portraits/women/44.jpg" alt="User profile">
158
+ <div class="ml-3">
159
+ <p class="text-sm font-medium">Sarah Johnson</p>
160
+ <p class="text-xs text-indigo-200">Admin</p>
161
+ </div>
162
+ </div>
163
+ </div>
164
+ </div>
165
+
166
+ <!-- Main content -->
167
+ <div class="flex-1 flex flex-col overflow-hidden">
168
+ <!-- Top navigation -->
169
+ <header class="bg-white border-b border-gray-200 flex-shrink-0">
170
+ <div class="flex items-center justify-between px-6 py-3">
171
+ <div class="flex items-center">
172
+ <button class="md:hidden mr-4 text-gray-500 focus:outline-none">
173
+ <i class="fas fa-bars"></i>
174
+ </button>
175
+ <h1 class="text-xl font-semibold text-gray-800">Compliance Dashboard</h1>
176
+ </div>
177
+ <div class="flex items-center space-x-4">
178
+ <button class="p-2 text-gray-500 rounded-full hover:bg-gray-100 focus:outline-none">
179
+ <i class="fas fa-bell"></i>
180
+ </button>
181
+ <button class="p-2 text-gray-500 rounded-full hover:bg-gray-100 focus:outline-none">
182
+ <i class="fas fa-question-circle"></i>
183
+ </button>
184
+ <div class="relative">
185
+ <button class="flex items-center focus:outline-none">
186
+ <img class="w-8 h-8 rounded-full" src="https://randomuser.me/api/portraits/women/44.jpg" alt="User profile">
187
+ </button>
188
+ </div>
189
+ </div>
190
+ </div>
191
+ </header>
192
+
193
+ <!-- Main content area -->
194
+ <main class="flex-1 overflow-y-auto p-6 bg-gray-50">
195
+ <div class="mb-6">
196
+ <div class="flex flex-col md:flex-row md:items-center md:justify-between">
197
+ <div>
198
+ <h2 class="text-2xl font-bold text-gray-800">Welcome back, Sarah</h2>
199
+ <p class="text-gray-600">Here's your compliance overview for today</p>
200
+ </div>
201
+ <div class="mt-4 md:mt-0">
202
+ <button id="newTaskBtn" class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-lg flex items-center">
203
+ <i class="fas fa-plus mr-2"></i>
204
+ New Compliance Task
205
+ </button>
206
+ </div>
207
+ </div>
208
+ </div>
209
+
210
+ <!-- Stats cards -->
211
+ <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8" id="statsContainer">
212
+ <!-- Stats will be loaded dynamically -->
213
+ </div>
214
+
215
+ <!-- Main content grid -->
216
+ <div class="grid grid-cols-1 lg:grid-cols-3 gap-6 mb-8">
217
+ <!-- Compliance Frameworks -->
218
+ <div class="lg:col-span-2">
219
+ <div class="compliance-card bg-white rounded-xl shadow-sm p-6 h-full">
220
+ <div class="flex items-center justify-between mb-6">
221
+ <h3 class="text-lg font-semibold text-gray-800">Compliance Frameworks</h3>
222
+ <button id="viewAllFrameworksBtn" class="text-sm text-indigo-600 hover:text-indigo-800">View All</button>
223
+ </div>
224
+
225
+ <div class="space-y-4" id="frameworksContainer">
226
+ <!-- Frameworks will be loaded dynamically -->
227
+ </div>
228
+
229
+ <button id="addFrameworkBtn" class="w-full mt-4 py-2 border-2 border-dashed border-gray-300 rounded-lg text-gray-500 hover:border-indigo-300 hover:text-indigo-500 transition-colors">
230
+ <i class="fas fa-plus mr-2"></i> Add New Framework
231
+ </button>
232
+ </div>
233
+ </div>
234
+
235
+ <!-- Upcoming Tasks -->
236
+ <div>
237
+ <div class="compliance-card bg-white rounded-xl shadow-sm p-6 h-full">
238
+ <div class="flex items-center justify-between mb-6">
239
+ <h3 class="text-lg font-semibold text-gray-800">Upcoming Tasks</h3>
240
+ <button id="viewAllTasksBtn" class="text-sm text-indigo-600 hover:text-indigo-800">View All</button>
241
+ </div>
242
+
243
+ <div class="space-y-4" id="tasksContainer">
244
+ <!-- Tasks will be loaded dynamically -->
245
+ </div>
246
+ </div>
247
+ </div>
248
+ </div>
249
+
250
+ <!-- Recent Activity and Document Library -->
251
+ <div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
252
+ <!-- Recent Activity -->
253
+ <div>
254
+ <div class="compliance-card bg-white rounded-xl shadow-sm p-6 h-full">
255
+ <div class="flex items-center justify-between mb-6">
256
+ <h3 class="text-lg font-semibold text-gray-800">Recent Activity</h3>
257
+ <button id="viewAllActivityBtn" class="text-sm text-indigo-600 hover:text-indigo-800">View All</button>
258
+ </div>
259
+
260
+ <div class="space-y-4" id="activityContainer">
261
+ <!-- Activity will be loaded dynamically -->
262
+ </div>
263
+ </div>
264
+ </div>
265
+
266
+ <!-- Document Library -->
267
+ <div class="lg:col-span-2">
268
+ <div class="compliance-card bg-white rounded-xl shadow-sm p-6 h-full">
269
+ <div class="flex items-center justify-between mb-6">
270
+ <h3 class="text-lg font-semibold text-gray-800">Document Library</h3>
271
+ <div class="flex space-x-2">
272
+ <button id="viewAllDocumentsBtn" class="text-sm text-indigo-600 hover:text-indigo-800">View All</button>
273
+ <button id="uploadDocumentBtn" class="text-sm text-indigo-600 hover:text-indigo-800 flex items-center">
274
+ <i class="fas fa-plus mr-1"></i> Upload
275
+ </button>
276
+ </div>
277
+ </div>
278
+
279
+ <div class="overflow-x-auto">
280
+ <table class="min-w-full divide-y divide-gray-200">
281
+ <thead class="bg-gray-50">
282
+ <tr>
283
+ <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Document</th>
284
+ <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Type</th>
285
+ <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Last Updated</th>
286
+ <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
287
+ <th scope="col" class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
288
+ </tr>
289
+ </thead>
290
+ <tbody class="bg-white divide-y divide-gray-200" id="documentsContainer">
291
+ <!-- Documents will be loaded dynamically -->
292
+ </tbody>
293
+ </table>
294
+ </div>
295
+ </div>
296
+ </div>
297
+ </div>
298
+ </main>
299
+ </div>
300
+ </div>
301
+
302
+ <!-- Modals -->
303
+ <div id="newTaskModal" class="fixed inset-0 z-50 flex items-center justify-center hidden modal-overlay">
304
+ <div class="modal bg-white rounded-lg shadow-xl w-full max-w-md transform transition-all">
305
+ <div class="p-6">
306
+ <div class="flex justify-between items-center mb-4">
307
+ <h3 class="text-lg font-semibold text-gray-800">Create New Task</h3>
308
+ <button id="closeTaskModalBtn" class="text-gray-500 hover:text-gray-700">
309
+ <i class="fas fa-times"></i>
310
+ </button>
311
+ </div>
312
+ <form id="taskForm">
313
+ <div class="mb-4">
314
+ <label for="taskTitle" class="block text-sm font-medium text-gray-700 mb-1">Task Title</label>
315
+ <input type="text" id="taskTitle" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500" required>
316
+ </div>
317
+ <div class="mb-4">
318
+ <label for="taskDescription" class="block text-sm font-medium text-gray-700 mb-1">Description</label>
319
+ <textarea id="taskDescription" rows="3" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500"></textarea>
320
+ </div>
321
+ <div class="grid grid-cols-2 gap-4 mb-4">
322
+ <div>
323
+ <label for="taskDueDate" class="block text-sm font-medium text-gray-700 mb-1">Due Date</label>
324
+ <input type="date" id="taskDueDate" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500" required>
325
+ </div>
326
+ <div>
327
+ <label for="taskPriority" class="block text-sm font-medium text-gray-700 mb-1">Priority</label>
328
+ <select id="taskPriority" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500">
329
+ <option value="low">Low</option>
330
+ <option value="medium">Medium</option>
331
+ <option value="high">High</option>
332
+ </select>
333
+ </div>
334
+ </div>
335
+ <div class="mb-4">
336
+ <label for="taskFramework" class="block text-sm font-medium text-gray-700 mb-1">Related Framework</label>
337
+ <select id="taskFramework" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500">
338
+ <option value="">None</option>
339
+ <!-- Options will be populated dynamically -->
340
+ </select>
341
+ </div>
342
+ <div class="flex justify-end space-x-3">
343
+ <button type="button" id="cancelTaskBtn" class="px-4 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50">Cancel</button>
344
+ <button type="submit" class="px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700">Create Task</button>
345
+ </div>
346
+ </form>
347
+ </div>
348
+ </div>
349
+ </div>
350
+
351
+ <div id="newFrameworkModal" class="fixed inset-0 z-50 flex items-center justify-center hidden modal-overlay">
352
+ <div class="modal bg-white rounded-lg shadow-xl w-full max-w-md transform transition-all">
353
+ <div class="p-6">
354
+ <div class="flex justify-between items-center mb-4">
355
+ <h3 class="text-lg font-semibold text-gray-800">Add New Framework</h3>
356
+ <button id="closeFrameworkModalBtn" class="text-gray-500 hover:text-gray-700">
357
+ <i class="fas fa-times"></i>
358
+ </button>
359
+ </div>
360
+ <form id="frameworkForm">
361
+ <div class="mb-4">
362
+ <label for="frameworkName" class="block text-sm font-medium text-gray-700 mb-1">Framework Name</label>
363
+ <input type="text" id="frameworkName" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500" required>
364
+ </div>
365
+ <div class="mb-4">
366
+ <label for="frameworkDescription" class="block text-sm font-medium text-gray-700 mb-1">Description</label>
367
+ <textarea id="frameworkDescription" rows="3" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500"></textarea>
368
+ </div>
369
+ <div class="mb-4">
370
+ <label for="frameworkType" class="block text-sm font-medium text-gray-700 mb-1">Type</label>
371
+ <select id="frameworkType" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500">
372
+ <option value="gdpr">GDPR</option>
373
+ <option value="hipaa">HIPAA</option>
374
+ <option value="soc2">SOC 2</option>
375
+ <option value="iso27001">ISO 27001</option>
376
+ <option value="pci-dss">PCI-DSS</option>
377
+ <option value="other">Other</option>
378
+ </select>
379
+ </div>
380
+ <div class="grid grid-cols-2 gap-4 mb-4">
381
+ <div>
382
+ <label for="frameworkStartDate" class="block text-sm font-medium text-gray-700 mb-1">Start Date</label>
383
+ <input type="date" id="frameworkStartDate" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500" required>
384
+ </div>
385
+ <div>
386
+ <label for="frameworkReviewDate" class="block text-sm font-medium text-gray-700 mb-1">Next Review</label>
387
+ <input type="date" id="frameworkReviewDate" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500" required>
388
+ </div>
389
+ </div>
390
+ <div class="flex justify-end space-x-3">
391
+ <button type="button" id="cancelFrameworkBtn" class="px-4 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50">Cancel</button>
392
+ <button type="submit" class="px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700">Add Framework</button>
393
+ </div>
394
+ </form>
395
+ </div>
396
+ </div>
397
+ </div>
398
+
399
+ <div id="uploadDocumentModal" class="fixed inset-0 z-50 flex items-center justify-center hidden modal-overlay">
400
+ <div class="modal bg-white rounded-lg shadow-xl w-full max-w-md transform transition-all">
401
+ <div class="p-6">
402
+ <div class="flex justify-between items-center mb-4">
403
+ <h3 class="text-lg font-semibold text-gray-800">Upload Document</h3>
404
+ <button id="closeDocumentModalBtn" class="text-gray-500 hover:text-gray-700">
405
+ <i class="fas fa-times"></i>
406
+ </button>
407
+ </div>
408
+ <form id="documentForm">
409
+ <div class="mb-4">
410
+ <label for="documentName" class="block text-sm font-medium text-gray-700 mb-1">Document Name</label>
411
+ <input type="text" id="documentName" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500" required>
412
+ </div>
413
+ <div class="mb-4">
414
+ <label for="documentType" class="block text-sm font-medium text-gray-700 mb-1">Type</label>
415
+ <select id="documentType" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500">
416
+ <option value="policy">Policy</option>
417
+ <option value="procedure">Procedure</option>
418
+ <option value="template">Template</option>
419
+ <option value="report">Report</option>
420
+ <option value="agreement">Agreement</option>
421
+ </select>
422
+ </div>
423
+ <div class="mb-4">
424
+ <label for="documentFramework" class="block text-sm font-medium text-gray-700 mb-1">Related Framework</label>
425
+ <select id="documentFramework" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500">
426
+ <option value="">None</option>
427
+ <!-- Options will be populated dynamically -->
428
+ </select>
429
+ </div>
430
+ <div class="mb-4">
431
+ <label for="documentFile" class="block text-sm font-medium text-gray-700 mb-1">File</label>
432
+ <div class="mt-1 flex justify-center px-6 pt-5 pb-6 border-2 border-gray-300 border-dashed rounded-md">
433
+ <div class="space-y-1 text-center">
434
+ <div class="flex text-sm text-gray-600">
435
+ <label for="documentFile" class="relative cursor-pointer bg-white rounded-md font-medium text-indigo-600 hover:text-indigo-500 focus-within:outline-none">
436
+ <span>Upload a file</span>
437
+ <input id="documentFile" name="documentFile" type="file" class="sr-only">
438
+ </label>
439
+ <p class="pl-1">or drag and drop</p>
440
+ </div>
441
+ <p class="text-xs text-gray-500">PDF, DOCX, XLSX up to 10MB</p>
442
+ </div>
443
+ </div>
444
+ </div>
445
+ <div class="flex justify-end space-x-3">
446
+ <button type="button" id="cancelDocumentBtn" class="px-4 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50">Cancel</button>
447
+ <button type="submit" class="px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700">Upload Document</button>
448
+ </div>
449
+ </form>
450
+ </div>
451
+ </div>
452
+ </div>
453
+
454
+ <script>
455
+ // Backend Simulator
456
+ class ComplianceHubBackend {
457
+ constructor() {
458
+ this.initStorage();
459
+ }
460
+
461
+ initStorage() {
462
+ if (!localStorage.getItem('complyhub_frameworks')) {
463
+ const defaultFrameworks = [
464
+ {
465
+ id: '1',
466
+ name: 'GDPR Compliance',
467
+ description: 'General Data Protection Regulation',
468
+ type: 'gdpr',
469
+ status: 'active',
470
+ controlsCompleted: 18,
471
+ totalControls: 22,
472
+ startDate: '2022-05-15',
473
+ nextAudit: '2023-05-15'
474
+ },
475
+ {
476
+ id: '2',
477
+ name: 'SOC 2 Type II',
478
+ description: 'Service Organization Controls',
479
+ type: 'soc2',
480
+ status: 'in-progress',
481
+ controlsCompleted: 32,
482
+ totalControls: 45,
483
+ startDate: '2022-08-30',
484
+ nextAudit: '2023-08-30'
485
+ },
486
+ {
487
+ id: '3',
488
+ name: 'HIPAA Compliance',
489
+ description: 'Health Insurance Portability and Accountability Act',
490
+ type: 'hipaa',
491
+ status: 'pending',
492
+ controlsCompleted: 12,
493
+ totalControls: 28,
494
+ startDate: '2022-11-05',
495
+ nextAudit: '2023-11-05'
496
+ }
497
+ ];
498
+ localStorage.setItem('complyhub_frameworks', JSON.stringify(defaultFrameworks));
499
+ }
500
+
501
+ if (!localStorage.getItem('complyhub_tasks')) {
502
+ const defaultTasks = [
503
+ {
504
+ id: '1',
505
+ title: 'Employee Security Training',
506
+ description: 'Quarterly security awareness training',
507
+ dueDate: '2023-04-01',
508
+ status: 'overdue',
509
+ priority: 'high',
510
+ frameworkId: '1',
511
+ participants: 12
512
+ },
513
+ {
514
+ id: '2',
515
+ title: 'Vendor Risk Assessment',
516
+ description: 'Annual review of third-party vendors',
517
+ dueDate: '2023-04-15',
518
+ status: 'due-soon',
519
+ priority: 'medium',
520
+ frameworkId: '2',
521
+ vendors: 5
522
+ },
523
+ {
524
+ id: '3',
525
+ title: 'Password Policy Update',
526
+ description: 'Implement new password requirements',
527
+ dueDate: '2023-05-01',
528
+ status: 'upcoming',
529
+ priority: 'medium',
530
+ frameworkId: '1',
531
+ participants: 'all'
532
+ },
533
+ {
534
+ id: '4',
535
+ title: 'Data Backup Test',
536
+ description: 'Quarterly backup restoration test',
537
+ dueDate: '2023-05-10',
538
+ status: 'scheduled',
539
+ priority: 'low',
540
+ frameworkId: '3',
541
+ team: 'IT Team'
542
+ }
543
+ ];
544
+ localStorage.setItem('complyhub_tasks', JSON.stringify(defaultTasks));
545
+ }
546
+
547
+ if (!localStorage.getItem('complyhub_documents')) {
548
+ const defaultDocuments = [
549
+ {
550
+ id: '1',
551
+ name: 'Information Security Policy',
552
+ version: 'v3.2',
553
+ type: 'policy',
554
+ status: 'approved',
555
+ frameworkId: '1',
556
+ lastUpdated: '2023-03-28',
557
+ fileType: 'pdf'
558
+ },
559
+ {
560
+ id: '2',
561
+ name: 'Employee Handbook',
562
+ version: '2023 Edition',
563
+ type: 'handbook',
564
+ status: 'approved',
565
+ frameworkId: '',
566
+ lastUpdated: '2023-03-15',
567
+ fileType: 'docx'
568
+ },
569
+ {
570
+ id: '3',
571
+ name: 'Vendor Risk Assessment',
572
+ version: 'Q1 2023',
573
+ type: 'assessment',
574
+ status: 'in-review',
575
+ frameworkId: '2',
576
+ lastUpdated: '2023-03-10',
577
+ fileType: 'xlsx'
578
+ },
579
+ {
580
+ id: '4',
581
+ name: 'Data Processing Agreement',
582
+ version: 'Template',
583
+ type: 'contract',
584
+ status: 'approved',
585
+ frameworkId: '1',
586
+ lastUpdated: '2023-02-28',
587
+ fileType: 'docx'
588
+ },
589
+ {
590
+ id: '5',
591
+ name: 'Security Awareness Training',
592
+ version: 'Q2 2023',
593
+ type: 'presentation',
594
+ status: 'draft',
595
+ frameworkId: '3',
596
+ lastUpdated: '2023-02-15',
597
+ fileType: 'pptx'
598
+ }
599
+ ];
600
+ localStorage.setItem('complyhub_documents', JSON.stringify(defaultDocuments));
601
+ }
602
+
603
+ if (!localStorage.getItem('complyhub_activity')) {
604
+ const defaultActivity = [
605
+ {
606
+ id: '1',
607
+ type: 'document',
608
+ action: 'uploaded',
609
+ user: 'Michael Chen',
610
+ item: 'Incident Response Plan v2.3',
611
+ timestamp: new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString()
612
+ },
613
+ {
614
+ id: '2',
615
+ type: 'task',
616
+ action: 'completed',
617
+ user: 'Sarah Johnson',
618
+ item: 'Quarterly Security Audit',
619
+ timestamp: new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString()
620
+ },
621
+ {
622
+ id: '3',
623
+ type: 'comment',
624
+ action: 'added',
625
+ user: 'David Wilson',
626
+ item: 'Privacy Policy Update',
627
+ timestamp: new Date(Date.now() - 28 * 60 * 60 * 1000).toISOString()
628
+ },
629
+ {
630
+ id: '4',
631
+ type: 'task',
632
+ action: 'assigned',
633
+ user: 'System',
634
+ item: 'Vendor Risk Assessment',
635
+ assignedTo: 'Lisa Rodriguez',
636
+ timestamp: new Date(Date.now() - 5 * 24 * 60 * 60 * 1000).toISOString()
637
+ },
638
+ {
639
+ id: '5',
640
+ type: 'task',
641
+ action: 'flagged',
642
+ user: 'System',
643
+ item: 'Employee Security Training',
644
+ status: 'overdue',
645
+ timestamp: new Date(Date.now() - 6 * 24 * 60 * 60 * 1000).toISOString()
646
+ }
647
+ ];
648
+ localStorage.setItem('complyhub_activity', JSON.stringify(defaultActivity));
649
+ }
650
+
651
+ if (!localStorage.getItem('complyhub_stats')) {
652
+ const defaultStats = {
653
+ completedTasks: 24,
654
+ pendingTasks: 12,
655
+ overdueTasks: 3,
656
+ complianceScore: 87
657
+ };
658
+ localStorage.setItem('complyhub_stats', JSON.stringify(defaultStats));
659
+ }
660
+ }
661
+
662
+ // Framework methods
663
+ getFrameworks(limit = 3) {
664
+ const frameworks = JSON.parse(localStorage.getItem('complyhub_frameworks') || '[]');
665
+ return Promise.resolve(limit ? frameworks.slice(0, limit) : frameworks);
666
+ }
667
+
668
+ getFramework(id) {
669
+ const frameworks = JSON.parse(localStorage.getItem('complyhub_frameworks') || '[]');
670
+ return Promise.resolve(frameworks.find(f => f.id === id));
671
+ }
672
+
673
+ addFramework(framework) {
674
+ const frameworks = JSON.parse(localStorage.getItem('complyhub_frameworks') || '[]');
675
+ const newFramework = {
676
+ ...framework,
677
+ id: Date.now().toString(),
678
+ status: 'pending',
679
+ controlsCompleted: 0,
680
+ totalControls: 0
681
+ };
682
+ frameworks.push(newFramework);
683
+ localStorage.setItem('complyhub_frameworks', JSON.stringify(frameworks));
684
+ return Promise.resolve(newFramework);
685
+ }
686
+
687
+ updateFramework(id, updates) {
688
+ const frameworks = JSON.parse(localStorage.getItem('complyhub_frameworks') || '[]');
689
+ const index = frameworks.findIndex(f => f.id === id);
690
+ if (index !== -1) {
691
+ frameworks[index] = { ...frameworks[index], ...updates };
692
+ localStorage.setItem('complyhub_frameworks', JSON.stringify(frameworks));
693
+ return Promise.resolve(frameworks[index]);
694
+ }
695
+ return Promise.reject(new Error('Framework not found'));
696
+ }
697
+
698
+ // Task methods
699
+ getTasks(limit = 4) {
700
+ const tasks = JSON.parse(localStorage.getItem('complyhub_tasks') || '[]');
701
+ return Promise.resolve(limit ? tasks.slice(0, limit) : tasks);
702
+ }
703
+
704
+ addTask(task) {
705
+ const tasks = JSON.parse(localStorage.getItem('complyhub_tasks') || '[]');
706
+ const newTask = {
707
+ ...task,
708
+ id: Date.now().toString(),
709
+ status: this.getTaskStatus(task.dueDate)
710
+ };
711
+ tasks.push(newTask);
712
+ localStorage.setItem('complyhub_tasks', JSON.stringify(tasks));
713
+
714
+ // Add activity
715
+ this.addActivity({
716
+ type: 'task',
717
+ action: 'created',
718
+ user: 'Sarah Johnson',
719
+ item: task.title
720
+ });
721
+
722
+ // Update stats
723
+ this.updateStats();
724
+
725
+ return Promise.resolve(newTask);
726
+ }
727
+
728
+ completeTask(id) {
729
+ const tasks = JSON.parse(localStorage.getItem('complyhub_tasks') || '[]');
730
+ const index = tasks.findIndex(t => t.id === id);
731
+ if (index !== -1) {
732
+ tasks[index].status = 'completed';
733
+ localStorage.setItem('complyhub_tasks', JSON.stringify(tasks));
734
+
735
+ // Add activity
736
+ this.addActivity({
737
+ type: 'task',
738
+ action: 'completed',
739
+ user: 'Sarah Johnson',
740
+ item: tasks[index].title
741
+ });
742
+
743
+ // Update stats
744
+ this.updateStats();
745
+
746
+ return Promise.resolve(tasks[index]);
747
+ }
748
+ return Promise.reject(new Error('Task not found'));
749
+ }
750
+
751
+ getTaskStatus(dueDate) {
752
+ const today = new Date();
753
+ today.setHours(0, 0, 0, 0);
754
+ const due = new Date(dueDate);
755
+ const diffDays = Math.floor((due - today) / (1000 * 60 * 60 * 24));
756
+
757
+ if (diffDays < 0) return 'overdue';
758
+ if (diffDays <= 7) return 'due-soon';
759
+ return 'upcoming';
760
+ }
761
+
762
+ // Document methods
763
+ getDocuments(limit = 5) {
764
+ const documents = JSON.parse(localStorage.getItem('complyhub_documents') || '[]');
765
+ return Promise.resolve(limit ? documents.slice(0, limit) : documents);
766
+ }
767
+
768
+ addDocument(document) {
769
+ const documents = JSON.parse(localStorage.getItem('complyhub_documents') || '[]');
770
+ const newDocument = {
771
+ ...document,
772
+ id: Date.now().toString(),
773
+ lastUpdated: new Date().toISOString().split('T')[0],
774
+ status: 'draft'
775
+ };
776
+ documents.push(newDocument);
777
+ localStorage.setItem('complyhub_documents', JSON.stringify(documents));
778
+
779
+ // Add activity
780
+ this.addActivity({
781
+ type: 'document',
782
+ action: 'uploaded',
783
+ user: 'Sarah Johnson',
784
+ item: document.name
785
+ });
786
+
787
+ return Promise.resolve(newDocument);
788
+ }
789
+
790
+ // Activity methods
791
+ getActivity(limit = 5) {
792
+ const activity = JSON.parse(localStorage.getItem('complyhub_activity') || '[]');
793
+ return Promise.resolve(limit ? activity.slice(0, limit) : activity);
794
+ }
795
+
796
+ addActivity(activity) {
797
+ const activities = JSON.parse(localStorage.getItem('complyhub_activity') || '[]');
798
+ const newActivity = {
799
+ ...activity,
800
+ id: Date.now().toString(),
801
+ timestamp: new Date().toISOString()
802
+ };
803
+ activities.unshift(newActivity);
804
+ localStorage.setItem('complyhub_activity', JSON.stringify(activities));
805
+ return Promise.resolve(newActivity);
806
+ }
807
+
808
+ // Stats methods
809
+ getStats() {
810
+ const stats = JSON.parse(localStorage.getItem('complyhub_stats') || '{}');
811
+ return Promise.resolve(stats);
812
+ }
813
+
814
+ updateStats() {
815
+ const tasks = JSON.parse(localStorage.getItem('complyhub_tasks') || '[]');
816
+ const completedTasks = tasks.filter(t => t.status === 'completed').length;
817
+ const pendingTasks = tasks.filter(t => t.status !== 'completed').length;
818
+ const overdueTasks = tasks.filter(t => t.status === 'overdue').length;
819
+
820
+ // Simple algorithm to calculate compliance score
821
+ const frameworks = JSON.parse(localStorage.getItem('complyhub_frameworks') || '[]');
822
+ let totalControls = 0;
823
+ let completedControls = 0;
824
+
825
+ frameworks.forEach(f => {
826
+ totalControls += f.totalControls || 0;
827
+ completedControls += f.controlsCompleted || 0;
828
+ });
829
+
830
+ const complianceScore = totalControls > 0
831
+ ? Math.min(100, Math.round((completedControls / totalControls) * 100))
832
+ : 87; // Default score
833
+
834
+ const stats = {
835
+ completedTasks,
836
+ pendingTasks,
837
+ overdueTasks,
838
+ complianceScore
839
+ };
840
+
841
+ localStorage.setItem('complyhub_stats', JSON.stringify(stats));
842
+ return Promise.resolve(stats);
843
+ }
844
+ }
845
+
846
+ // UI Controller
847
+ class ComplianceHubUI {
848
+ constructor(backend) {
849
+ this.backend = backend;
850
+ this.initEventListeners();
851
+ this.loadData();
852
+ }
853
+
854
+ initEventListeners() {
855
+ // Modal buttons
856
+ document.getElementById('newTaskBtn').addEventListener('click', () => this.showNewTaskModal());
857
+ document.getElementById('closeTaskModalBtn').addEventListener('click', () => this.hideModal('newTaskModal'));
858
+ document.getElementById('cancelTaskBtn').addEventListener('click', () => this.hideModal('newTaskModal'));
859
+ document.getElementById('taskForm').addEventListener('submit', (e) => {
860
+ e.preventDefault();
861
+ this.createNewTask();
862
+ });
863
+
864
+ document.getElementById('addFrameworkBtn').addEventListener('click', () => this.showNewFrameworkModal());
865
+ document.getElementById('closeFrameworkModalBtn').addEventListener('click', () => this.hideModal('newFrameworkModal'));
866
+ document.getElementById('cancelFrameworkBtn').addEventListener('click', () => this.hideModal('newFrameworkModal'));
867
+ document.getElementById('frameworkForm').addEventListener('submit', (e) => {
868
+ e.preventDefault();
869
+ this.createNewFramework();
870
+ });
871
+
872
+ document.getElementById('uploadDocumentBtn').addEventListener('click', () => this.showUploadDocumentModal());
873
+ document.getElementById('closeDocumentModalBtn').addEventListener('click', () => this.hideModal('uploadDocumentModal'));
874
+ document.getElementById('cancelDocumentBtn').addEventListener('click', () => this.hideModal('uploadDocumentModal'));
875
+ document.getElementById('documentForm').addEventListener('submit', (e) => {
876
+ e.preventDefault();
877
+ this.uploadNewDocument();
878
+ });
879
+
880
+ // View all buttons
881
+ document.getElementById('viewAllFrameworksBtn').addEventListener('click', () => alert('View all frameworks clicked'));
882
+ document.getElementById('viewAllTasksBtn').addEventListener('click', () => alert('View all tasks clicked'));
883
+ document.getElementById('viewAllActivityBtn').addEventListener('click', () => alert('View all activity clicked'));
884
+ document.getElementById('viewAllDocumentsBtn').addEventListener('click', () => alert('View all documents clicked'));
885
+
886
+ // Mobile menu toggle
887
+ document.querySelector('.md\\:hidden').addEventListener('click', () => {
888
+ document.querySelector('.sidebar').classList.toggle('hidden');
889
+ });
890
+ }
891
+
892
+ showModal(modalId) {
893
+ document.getElementById(modalId).classList.remove('hidden');
894
+ setTimeout(() => {
895
+ document.getElementById(modalId).querySelector('.modal').classList.remove('opacity-0');
896
+ document.getElementById(modalId).querySelector('.modal').classList.remove('translate-y-4');
897
+ }, 10);
898
+ }
899
+
900
+ hideModal(modalId) {
901
+ document.getElementById(modalId).querySelector('.modal').classList.add('opacity-0');
902
+ document.getElementById(modalId).querySelector('.modal').classList.add('translate-y-4');
903
+ setTimeout(() => {
904
+ document.getElementById(modalId).classList.add('hidden');
905
+ }, 300);
906
+ }
907
+
908
+ showNewTaskModal() {
909
+ // Populate framework dropdown
910
+ this.backend.getFrameworks().then(frameworks => {
911
+ const select = document.getElementById('taskFramework');
912
+ select.innerHTML = '<option value="">None</option>';
913
+ frameworks.forEach(framework => {
914
+ const option = document.createElement('option');
915
+ option.value = framework.id;
916
+ option.textContent = framework.name;
917
+ select.appendChild(option);
918
+ });
919
+
920
+ // Set default due date to tomorrow
921
+ const tomorrow = new Date();
922
+ tomorrow.setDate(tomorrow.getDate() + 1);
923
+ document.getElementById('taskDueDate').valueAsDate = tomorrow;
924
+
925
+ this.showModal('newTaskModal');
926
+ });
927
+ }
928
+
929
+ showNewFrameworkModal() {
930
+ // Set default dates
931
+ const today = new Date();
932
+ document.getElementById('frameworkStartDate').valueAsDate = today;
933
+
934
+ const nextYear = new Date();
935
+ nextYear.setFullYear(nextYear.getFullYear() + 1);
936
+ document.getElementById('frameworkReviewDate').valueAsDate = nextYear;
937
+
938
+ this.showModal('newFrameworkModal');
939
+ }
940
+
941
+ showUploadDocumentModal() {
942
+ // Populate framework dropdown
943
+ this.backend.getFrameworks().then(frameworks => {
944
+ const select = document.getElementById('documentFramework');
945
+ select.innerHTML = '<option value="">None</option>';
946
+ frameworks.forEach(framework => {
947
+ const option = document.createElement('option');
948
+ option.value = framework.id;
949
+ option.textContent = framework.name;
950
+ select.appendChild(option);
951
+ });
952
+
953
+ this.showModal('uploadDocumentModal');
954
+ });
955
+ }
956
+
957
+ createNewTask() {
958
+ const title = document.getElementById('taskTitle').value;
959
+ const description = document.getElementById('taskDescription').value;
960
+ const dueDate = document.getElementById('taskDueDate').value;
961
+ const priority = document.getElementById('taskPriority').value;
962
+ const frameworkId = document.getElementById('taskFramework').value;
963
+
964
+ const task = {
965
+ title,
966
+ description,
967
+ dueDate,
968
+ priority,
969
+ frameworkId
970
+ };
971
+
972
+ this.backend.addTask(task).then(() => {
973
+ this.hideModal('newTaskModal');
974
+ this.loadTasks();
975
+ this.loadStats();
976
+ document.getElementById('taskForm').reset();
977
+ });
978
+ }
979
+
980
+ createNewFramework() {
981
+ const name = document.getElementById('frameworkName').value;
982
+ const description = document.getElementById('frameworkDescription').value;
983
+ const type = document.getElementById('frameworkType').value;
984
+ const startDate = document.getElementById('frameworkStartDate').value;
985
+ const reviewDate = document.getElementById('frameworkReviewDate').value;
986
+
987
+ const framework = {
988
+ name,
989
+ description,
990
+ type,
991
+ startDate,
992
+ nextAudit: reviewDate
993
+ };
994
+
995
+ this.backend.addFramework(framework).then(() => {
996
+ this.hideModal('newFrameworkModal');
997
+ this.loadFrameworks();
998
+ this.loadStats();
999
+ document.getElementById('frameworkForm').reset();
1000
+ });
1001
+ }
1002
+
1003
+ uploadNewDocument() {
1004
+ const name = document.getElementById('documentName').value;
1005
+ const type = document.getElementById('documentType').value;
1006
+ const frameworkId = document.getElementById('documentFramework').value;
1007
+ const file = document.getElementById('documentFile').files[0];
1008
+
1009
+ if (!file) {
1010
+ alert('Please select a file to upload');
1011
+ return;
1012
+ }
1013
+
1014
+ // Get file extension
1015
+ const fileType = file.name.split('.').pop().toLowerCase();
1016
+
1017
+ const document = {
1018
+ name,
1019
+ type,
1020
+ frameworkId,
1021
+ fileType
1022
+ };
1023
+
1024
+ this.backend.addDocument(document).then(() => {
1025
+ this.hideModal('uploadDocumentModal');
1026
+ this.loadDocuments();
1027
+ document.getElementById('documentForm').reset();
1028
+ });
1029
+ }
1030
+
1031
+ loadData() {
1032
+ this.loadStats();
1033
+ this.loadFrameworks();
1034
+ this.loadTasks();
1035
+ this.loadActivity();
1036
+ this.loadDocuments();
1037
+ }
1038
+
1039
+ loadStats() {
1040
+ this.backend.getStats().then(stats => {
1041
+ const statsContainer = document.getElementById('statsContainer');
1042
+ statsContainer.innerHTML = `
1043
+ <div class="compliance-card bg-white rounded-xl shadow-sm p-6 border-l-4 border-green-500">
1044
+ <div class="flex items-center justify-between">
1045
+ <div>
1046
+ <p class="text-sm font-medium text-gray-500">Completed Tasks</p>
1047
+ <p class="text-2xl font-bold text-gray-800 mt-1">${stats.completedTasks || 0}</p>
1048
+ <p class="text-xs text-green-500 mt-1">
1049
+ <i class="fas fa-arrow-up mr-1"></i> 5 from last week
1050
+ </p>
1051
+ </div>
1052
+ <div class="bg-green-100 p-3 rounded-full">
1053
+ <i class="fas fa-check-circle text-green-600 text-xl"></i>
1054
+ </div>
1055
+ </div>
1056
+ </div>
1057
+
1058
+ <div class="compliance-card bg-white rounded-xl shadow-sm p-6 border-l-4 border-blue-500">
1059
+ <div class="flex items-center justify-between">
1060
+ <div>
1061
+ <p class="text-sm font-medium text-gray-500">Pending Tasks</p>
1062
+ <p class="text-2xl font-bold text-gray-800 mt-1">${stats.pendingTasks || 0}</p>
1063
+ <p class="text-xs text-blue-500 mt-1">
1064
+ <i class="fas fa-arrow-down mr-1"></i> 3 from last week
1065
+ </p>
1066
+ </div>
1067
+ <div class="bg-blue-100 p-3 rounded-full">
1068
+ <i class="fas fa-clock text-blue-600 text-xl"></i>
1069
+ </div>
1070
+ </div>
1071
+ </div>
1072
+
1073
+ <div class="compliance-card bg-white rounded-xl shadow-sm p-6 border-l-4 border-yellow-500">
1074
+ <div class="flex items-center justify-between">
1075
+ <div>
1076
+ <p class="text-sm font-medium text-gray-500">Overdue Tasks</p>
1077
+ <p class="text-2xl font-bold text-gray-800 mt-1">${stats.overdueTasks || 0}</p>
1078
+ <p class="text-xs text-yellow-500 mt-1">
1079
+ <i class="fas fa-exclamation-triangle mr-1"></i> Needs attention
1080
+ </p>
1081
+ </div>
1082
+ <div class="bg-yellow-100 p-3 rounded-full">
1083
+ <i class="fas fa-exclamation text-yellow-600 text-xl"></i>
1084
+ </div>
1085
+ </div>
1086
+ </div>
1087
+
1088
+ <div class="compliance-card bg-white rounded-xl shadow-sm p-6 border-l-4 border-purple-500">
1089
+ <div class="flex items-center justify-between">
1090
+ <div>
1091
+ <p class="text-sm font-medium text-gray-500">Compliance Score</p>
1092
+ <p class="text-2xl font-bold text-gray-800 mt-1">${stats.complianceScore || 0}%</p>
1093
+ <p class="text-xs text-purple-500 mt-1">
1094
+ <i class="fas fa-chart-line mr-1"></i> 2% improvement
1095
+ </p>
1096
+ </div>
1097
+ <div class="relative w-12 h-12">
1098
+ <svg class="w-full h-full" viewBox="0 0 36 36">
1099
+ <path
1100
+ d="M18 2.0845
1101
+ a 15.9155 15.9155 0 0 1 0 31.831
1102
+ a 15.9155 15.9155 0 0 1 0 -31.831"
1103
+ fill="none"
1104
+ stroke="#e5e7eb"
1105
+ stroke-width="3"
1106
+ />
1107
+ <path
1108
+ d="M18 2.0845
1109
+ a 15.9155 15.9155 0 0 1 0 31.831
1110
+ a 15.9155 15.9155 0 0 1 0 -31.831"
1111
+ fill="none"
1112
+ stroke="#8b5cf6"
1113
+ stroke-width="3"
1114
+ stroke-dasharray="${stats.complianceScore || 0}, 100"
1115
+ class="progress-ring__circle"
1116
+ />
1117
+ </svg>
1118
+ <div class="absolute inset-0 flex items-center justify-center">
1119
+ <span class="text-xs font-bold text-purple-600">${stats.complianceScore || 0}%</span>
1120
+ </div>
1121
+ </div>
1122
+ </div>
1123
+ </div>
1124
+ `;
1125
+
1126
+ // Animate progress rings
1127
+ const progressRings = document.querySelectorAll('.progress-ring__circle');
1128
+ progressRings.forEach(ring => {
1129
+ const radius = ring.r.baseVal.value;
1130
+ const circumference = radius * 2 * Math.PI;
1131
+ ring.style.strokeDasharray = `${circumference} ${circumference}`;
1132
+ ring.style.strokeDashoffset = circumference;
1133
+
1134
+ const progress = parseInt(ring.parentElement.nextElementSibling.textContent);
1135
+ const offset = circumference - progress / 100 * circumference;
1136
+ ring.style.strokeDashoffset = offset;
1137
+ });
1138
+ });
1139
+ }
1140
+
1141
+ loadFrameworks() {
1142
+ this.backend.getFrameworks().then(frameworks => {
1143
+ const frameworksContainer = document.getElementById('frameworksContainer');
1144
+ frameworksContainer.innerHTML = '';
1145
+
1146
+ frameworks.forEach(framework => {
1147
+ const statusClass = {
1148
+ 'active': 'bg-green-100 text-green-800',
1149
+ 'in-progress': 'bg-yellow-100 text-yellow-800',
1150
+ 'pending': 'bg-blue-100 text-blue-800'
1151
+ }[framework.status] || 'bg-gray-100 text-gray-800';
1152
+
1153
+ const statusText = {
1154
+ 'active': 'Active',
1155
+ 'in-progress': 'In Progress',
1156
+ 'pending': 'Pending Review'
1157
+ }[framework.status] || framework.status;
1158
+
1159
+ const iconClass = {
1160
+ 'gdpr': 'fas fa-lock text-blue-600',
1161
+ 'hipaa': 'fas fa-heartbeat text-green-600',
1162
+ 'soc2': 'fas fa-shield-alt text-purple-600'
1163
+ }[framework.type] || 'fas fa-clipboard-list text-indigo-600';
1164
+
1165
+ const iconBgClass = {
1166
+ 'gdpr': 'bg-blue-100',
1167
+ 'hipaa': 'bg-green-100',
1168
+ 'soc2': 'bg-purple-100'
1169
+ }[framework.type] || 'bg-indigo-100';
1170
+
1171
+ const frameworkElement = document.createElement('div');
1172
+ frameworkElement.className = 'flex items-start p-4 border border-gray-200 rounded-lg';
1173
+ frameworkElement.innerHTML = `
1174
+ <div class="${iconBgClass} p-2 rounded-lg mr-4">
1175
+ <i class="${iconClass} text-xl"></i>
1176
+ </div>
1177
+ <div class="flex-1">
1178
+ <div class="flex items-center justify-between">
1179
+ <h4 class="font-medium text-gray-800">${framework.name}</h4>
1180
+ <span class="text-xs px-2 py-1 ${statusClass} rounded-full">${statusText}</span>
1181
+ </div>
1182
+ <p class="text-sm text-gray-600 mt-1">${framework.description}</p>
1183
+ <div class="mt-3 flex items-center text-xs text-gray-500">
1184
+ <span class="mr-4"><i class="fas fa-check-circle text-green-500 mr-1"></i> ${framework.controlsCompleted}/${framework.totalControls} controls</span>
1185
+ <span><i class="fas fa-calendar-alt mr-1"></i> Next audit: ${new Date(framework.nextAudit).toLocaleDateString()}</span>
1186
+ </div>
1187
+ </div>
1188
+ `;
1189
+
1190
+ frameworkElement.addEventListener('click', () => {
1191
+ alert(`View details for ${framework.name}`);
1192
+ });
1193
+
1194
+ frameworksContainer.appendChild(frameworkElement);
1195
+ });
1196
+ });
1197
+ }
1198
+
1199
+ loadTasks() {
1200
+ this.backend.getTasks().then(tasks => {
1201
+ const tasksContainer = document.getElementById('tasksContainer');
1202
+ tasksContainer.innerHTML = '';
1203
+
1204
+ tasks.forEach(task => {
1205
+ const statusClass = {
1206
+ 'overdue': 'bg-red-100 text-red-800',
1207
+ 'due-soon': 'bg-yellow-100 text-yellow-800',
1208
+ 'upcoming': 'bg-blue-100 text-blue-800',
1209
+ 'scheduled': 'bg-green-100 text-green-800'
1210
+ }[task.status] || 'bg-gray-100 text-gray-800';
1211
+
1212
+ const statusText = {
1213
+ 'overdue': 'Overdue',
1214
+ 'due-soon': 'Due Soon',
1215
+ 'upcoming': 'Upcoming',
1216
+ 'scheduled': 'Scheduled'
1217
+ }[task.status] || task.status;
1218
+
1219
+ const borderClass = {
1220
+ 'overdue': 'border-red-500 bg-red-50',
1221
+ 'due-soon': 'border-yellow-500 bg-yellow-50',
1222
+ 'upcoming': 'border-blue-500 bg-blue-50',
1223
+ 'scheduled': 'border-green-500 bg-green-50'
1224
+ }[task.status] || 'border-gray-300 bg-gray-50';
1225
+
1226
+ const taskElement = document.createElement('div');
1227
+ taskElement.className = `p-3 border-l-4 ${borderClass} rounded-r-lg`;
1228
+ taskElement.innerHTML = `
1229
+ <div class="flex justify-between items-start">
1230
+ <div>
1231
+ <h4 class="font-medium text-gray-800">${task.title}</h4>
1232
+ <p class="text-sm text-gray-600 mt-1">${task.description}</p>
1233
+ </div>
1234
+ <span class="text-xs px-2 py-1 ${statusClass} rounded-full">${statusText}</span>
1235
+ </div>
1236
+ <div class="mt-3 flex items-center text-xs text-gray-500">
1237
+ <span class="mr-3"><i class="fas fa-calendar${task.status === 'overdue' ? '-times text-red-500' : task.status === 'due-soon' ? '-day text-yellow-500' : task.status === 'scheduled' ? '-check text-green-500' : ' text-blue-500'} mr-1"></i> Due: ${new Date(task.dueDate).toLocaleDateString()}</span>
1238
+ <span><i class="${task.participants === 'all' ? 'fas fa-users' : task.vendors ? 'fas fa-building' : task.team ? 'fas fa-user-tie' : 'fas fa-user'} mr-1"></i> ${task.participants || task.vendors || task.team || '1 participant'}</span>
1239
+ </div>
1240
+ `;
1241
+
1242
+ taskElement.addEventListener('click', () => {
1243
+ alert(`View details for ${task.title}`);
1244
+ });
1245
+
1246
+ tasksContainer.appendChild(taskElement);
1247
+ });
1248
+ });
1249
+ }
1250
+
1251
+ loadActivity() {
1252
+ this.backend.getActivity().then(activity => {
1253
+ const activityContainer = document.getElementById('activityContainer');
1254
+ activityContainer.innerHTML = '';
1255
+
1256
+ activity.forEach(item => {
1257
+ const iconClass = {
1258
+ 'document': 'fas fa-file-alt',
1259
+ 'task': item.action === 'completed' ? 'fas fa-check-circle' : 'fas fa-tasks',
1260
+ 'comment': 'fas fa-comment-alt'
1261
+ }[item.type] || 'fas fa-info-circle';
1262
+
1263
+ const iconBgClass = {
1264
+ 'document': 'bg-indigo-100',
1265
+ 'task': item.action === 'completed' ? 'bg-green-100' : 'bg-purple-100',
1266
+ 'comment': 'bg-blue-100'
1267
+ }[item.type] || 'bg-gray-100';
1268
+
1269
+ const iconColorClass = {
1270
+ 'document': 'text-indigo-600',
1271
+ 'task': item.action === 'completed' ? 'text-green-600' : 'text-purple-600',
1272
+ 'comment': 'text-blue-600'
1273
+ }[item.type] || 'text-gray-600';
1274
+
1275
+ const actionText = {
1276
+ 'uploaded': 'uploaded',
1277
+ 'completed': 'completed',
1278
+ 'added': 'added a comment on',
1279
+ 'assigned': 'assigned',
1280
+ 'flagged': 'flagged as overdue'
1281
+ }[item.action] || item.action;
1282
+
1283
+ const assignedText = item.assignedTo ? ` to ${item.assignedTo}` : '';
1284
+
1285
+ const activityElement = document.createElement('div');
1286
+ activityElement.className = 'flex';
1287
+ activityElement.innerHTML = `
1288
+ <div class="mr-3">
1289
+ <div class="${iconBgClass} p-2 rounded-full">
1290
+ <i class="${iconClass} ${iconColorClass}"></i>
1291
+ </div>
1292
+ </div>
1293
+ <div class="flex-1">
1294
+ <p class="text-sm text-gray-800">
1295
+ <span class="font-medium">${item.user}</span> ${actionText} <span class="font-medium">"${item.item}"</span>${assignedText}
1296
+ </p>
1297
+ <p class="text-xs text-gray-500 mt-1">${new Date(item.timestamp).toLocaleString()}</p>
1298
+ </div>
1299
+ `;
1300
+
1301
+ activityContainer.appendChild(activityElement);
1302
+ });
1303
+ });
1304
+ }
1305
+
1306
+ loadDocuments() {
1307
+ this.backend.getDocuments().then(documents => {
1308
+ const documentsContainer = document.getElementById('documentsContainer');
1309
+ documentsContainer.innerHTML = '';
1310
+
1311
+ documents.forEach(doc => {
1312
+ const statusClass = {
1313
+ 'approved': 'bg-green-100 text-green-800',
1314
+ 'in-review': 'bg-yellow-100 text-yellow-800',
1315
+ 'draft': 'bg-blue-100 text-blue-800'
1316
+ }[doc.status] || 'bg-gray-100 text-gray-800';
1317
+
1318
+ const statusText = {
1319
+ 'approved': 'Approved',
1320
+ 'in-review': 'In Review',
1321
+ 'draft': 'Draft'
1322
+ }[doc.status] || doc.status;
1323
+
1324
+ const typeClass = {
1325
+ 'policy': 'bg-blue-100 text-blue-800',
1326
+ 'handbook': 'bg-purple-100 text-purple-800',
1327
+ 'assessment': 'bg-yellow-100 text-yellow-800',
1328
+ 'contract': 'bg-blue-100 text-blue-800',
1329
+ 'presentation': 'bg-gray-100 text-gray-800'
1330
+ }[doc.type] || 'bg-gray-100 text-gray-800';
1331
+
1332
+ const typeText = {
1333
+ 'policy': 'Policy',
1334
+ 'handbook': 'Handbook',
1335
+ 'assessment': 'Assessment',
1336
+ 'contract': 'Contract',
1337
+ 'presentation': 'Presentation'
1338
+ }[doc.type] || doc.type;
1339
+
1340
+ const iconClass = {
1341
+ 'pdf': 'fas fa-file-pdf text-red-600',
1342
+ 'docx': 'fas fa-file-word text-blue-600',
1343
+ 'xlsx': 'fas fa-file-excel text-green-600',
1344
+ 'pptx': 'fas fa-file-powerpoint text-orange-600'
1345
+ }[doc.fileType] || 'fas fa-file text-gray-600';
1346
+
1347
+ const iconBgClass = {
1348
+ 'pdf': 'bg-red-100',
1349
+ 'docx': 'bg-blue-100',
1350
+ 'xlsx': 'bg-green-100',
1351
+ 'pptx': 'bg-orange-100'
1352
+ }[doc.fileType] || 'bg-gray-100';
1353
+
1354
+ const docElement = document.createElement('tr');
1355
+ docElement.className = 'hover:bg-gray-50';
1356
+ docElement.innerHTML = `
1357
+ <td class="px-6 py-4 whitespace-nowrap">
1358
+ <div class="flex items-center">
1359
+ <div class="flex-shrink-0 h-10 w-10 ${iconBgClass} rounded-lg flex items-center justify-center">
1360
+ <i class="${iconClass}"></i>
1361
+ </div>
1362
+ <div class="ml-4">
1363
+ <div class="text-sm font-medium text-gray-900">${doc.name}</div>
1364
+ <div class="text-sm text-gray-500">${doc.version || ''}</div>
1365
+ </div>
1366
+ </div>
1367
+ </td>
1368
+ <td class="px-6 py-4 whitespace-nowrap">
1369
+ <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full ${typeClass}">${typeText}</span>
1370
+ </td>
1371
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${new Date(doc.lastUpdated).toLocaleDateString()}</td>
1372
+ <td class="px-6 py-4 whitespace-nowrap">
1373
+ <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full ${statusClass}">${statusText}</span>
1374
+ </td>
1375
+ <td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
1376
+ <button class="text-indigo-600 hover:text-indigo-900 mr-3"><i class="fas fa-eye"></i></button>
1377
+ <button class="text-indigo-600 hover:text-indigo-900"><i class="fas fa-download"></i></button>
1378
+ </td>
1379
+ `;
1380
+
1381
+ documentsContainer.appendChild(docElement);
1382
+ });
1383
+ });
1384
+ }
1385
+ }
1386
+
1387
+ // Initialize the application
1388
+ document.addEventListener('DOMContentLoaded', function() {
1389
+ const backend = new ComplianceHubBackend();
1390
+ const ui = new ComplianceHubUI(backend);
1391
+
1392
+ // Animate compliance cards on load
1393
+ const complianceCards = document.querySelectorAll('.compliance-card');
1394
+ complianceCards.forEach((card, index) => {
1395
+ card.classList.add('opacity-0');
1396
+ setTimeout(() => {
1397
+ card.classList.remove('opacity-0');
1398
+ card.classList.add('opacity-100');
1399
+ }, 100 * index);
1400
+ });
1401
+ });
1402
+ </script>
1403
+ <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=LukasBe/complyhub" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
1404
+ </html>