manideepreddym commited on
Commit
c4a61a5
·
verified ·
1 Parent(s): 4a663fe

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +831 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Contractverse
3
- emoji:
4
- colorFrom: yellow
5
- colorTo: gray
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: contractverse
3
+ emoji: 🐳
4
+ colorFrom: purple
5
+ colorTo: yellow
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,831 @@
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>ContraGit App</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
+ .diff-add {
11
+ background-color: #dcfce7;
12
+ color: #16a34a;
13
+ padding: 0 2px;
14
+ border-radius: 2px;
15
+ display: inline;
16
+ }
17
+ .diff-del {
18
+ background-color: #fee2e2;
19
+ color: #dc2626;
20
+ text-decoration: line-through;
21
+ padding: 0 2px;
22
+ border-radius: 2px;
23
+ display: inline;
24
+ }
25
+ .diff-neutral {
26
+ color: #4b5563;
27
+ display: inline;
28
+ }
29
+ pre {
30
+ white-space: pre-wrap;
31
+ word-wrap: break-word;
32
+ }
33
+ .contract-content {
34
+ max-height: 200px;
35
+ overflow-y: auto;
36
+ }
37
+ .version-card {
38
+ transition: all 0.2s ease;
39
+ }
40
+ .version-card:hover {
41
+ transform: translateY(-2px);
42
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
43
+ }
44
+ .comment-bubble {
45
+ position: relative;
46
+ background: #f3f4f6;
47
+ border-radius: 0.5rem;
48
+ }
49
+ .comment-bubble:after {
50
+ content: '';
51
+ position: absolute;
52
+ top: -10px;
53
+ left: 15px;
54
+ border-width: 0 10px 10px;
55
+ border-style: solid;
56
+ border-color: #f3f4f6 transparent;
57
+ }
58
+ </style>
59
+ </head>
60
+ <body class="min-h-screen bg-gray-100 font-sans">
61
+ <div id="app">
62
+ <!-- Header -->
63
+ <header class="bg-white shadow-sm">
64
+ <nav class="container mx-auto px-4 sm:px-6 lg:px-8 py-3 flex items-center justify-between">
65
+ <h1 class="text-2xl font-bold text-blue-600 cursor-pointer" onclick="goBackToDashboard()">
66
+ <i class="fas fa-file-contract mr-2"></i>ContraGit App
67
+ </h1>
68
+ <div class="flex items-center space-x-4">
69
+ <button id="newContractBtn" class="hidden md:flex items-center bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition duration-200 shadow">
70
+ <i class="fas fa-plus mr-2"></i> New Contract
71
+ </button>
72
+ <div class="relative">
73
+ <img src="https://randomuser.me/api/portraits/men/32.jpg" alt="User" class="w-8 h-8 rounded-full cursor-pointer" id="userMenuBtn">
74
+ <div id="userMenu" class="hidden absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg py-1 z-10">
75
+ <a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Profile</a>
76
+ <a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Settings</a>
77
+ <a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Logout</a>
78
+ </div>
79
+ </div>
80
+ </div>
81
+ </nav>
82
+ </header>
83
+
84
+ <!-- Main Content -->
85
+ <main class="container mx-auto px-4 sm:px-6 lg:px-8 py-8">
86
+ <div id="dashboardView">
87
+ <!-- Dashboard content will be loaded here -->
88
+ </div>
89
+ </main>
90
+
91
+ <!-- Footer -->
92
+ <footer class="bg-gray-200 text-center py-4 mt-12">
93
+ <p class="text-sm text-gray-600">&copy; 2025 ContraGit App</p>
94
+ </footer>
95
+ </div>
96
+
97
+ <script>
98
+ // Mock Data
99
+ const MOCK_CONTRACTS = [
100
+ {
101
+ id: 1,
102
+ name: "Freelance Agreement - Alex & Client X",
103
+ createdAt: "2023-10-01T10:00:00Z",
104
+ updatedAt: "2023-10-01T11:00:00Z",
105
+ },
106
+ {
107
+ id: 2,
108
+ name: "NDA - Project Phoenix",
109
+ createdAt: "2023-09-15T14:30:00Z",
110
+ updatedAt: "2023-09-15T14:30:00Z",
111
+ },
112
+ ];
113
+
114
+ const MOCK_VERSIONS = {
115
+ 1: [
116
+ { id: 1, contractId: 1, content: "Payment: $500 due on completion.\nDeadline: October 30th.", createdAt: "2023-10-01T10:00:00Z", createdBy: "Alex" },
117
+ { id: 2, contractId: 1, content: "Payment: $600 due in two installments: $300 on start, $300 on completion.\nDeadline: October 30th.", createdAt: "2023-10-01T11:00:00Z", createdBy: "Alex" },
118
+ { id: 3, contractId: 1, content: "Payment: $600 due in two installments: $300 on start, $300 on completion.\nDeadline: November 5th.", createdAt: "2023-10-01T12:15:00Z", createdBy: "Jamie" },
119
+ ],
120
+ 2: [
121
+ { id: 4, contractId: 2, content: "Standard Non-Disclosure Agreement terms apply.", createdAt: "2023-09-15T14:30:00Z", createdBy: "System" },
122
+ ],
123
+ };
124
+
125
+ const MOCK_COMMENTS = {
126
+ 2: [
127
+ { id: 1, versionId: 2, userId: "Jamie", comment: "I agree with the new payment terms but need to clarify the deadline.", createdAt: "2023-10-01T12:00:00Z" },
128
+ ],
129
+ 3: [
130
+ { id: 2, versionId: 3, userId: "Alex", comment: "Deadline updated to Nov 5th. Please confirm.", createdAt: "2023-10-01T12:20:00Z" },
131
+ ]
132
+ };
133
+
134
+ // State
135
+ let currentView = 'dashboard';
136
+ let selectedContractId = null;
137
+ let compareVersionIds = { v1: null, v2: null };
138
+ let contracts = [];
139
+ let versions = {};
140
+ let comments = {};
141
+ let loading = true;
142
+ let error = null;
143
+
144
+ // Utility Functions
145
+ function formatDate(isoString) {
146
+ if (!isoString) return 'N/A';
147
+ try {
148
+ const date = new Date(isoString);
149
+ return date.toLocaleString('en-US', {
150
+ month: 'short',
151
+ day: 'numeric',
152
+ year: 'numeric',
153
+ hour: '2-digit',
154
+ minute: '2-digit'
155
+ });
156
+ } catch (e) {
157
+ return 'Invalid Date';
158
+ }
159
+ }
160
+
161
+ function calculateDiff(text1, text2) {
162
+ // This is a simplified diff algorithm - in a real app, use a proper diff library
163
+ const lines1 = text1.split('\n');
164
+ const lines2 = text2.split('\n');
165
+ const maxLength = Math.max(lines1.length, lines2.length);
166
+ let result = [];
167
+
168
+ for (let i = 0; i < maxLength; i++) {
169
+ const line1 = lines1[i] || '';
170
+ const line2 = lines2[i] || '';
171
+
172
+ if (line1 === line2) {
173
+ result.push({
174
+ value: line1,
175
+ added: false,
176
+ removed: false
177
+ });
178
+ } else {
179
+ if (line1) {
180
+ result.push({
181
+ value: line1,
182
+ added: false,
183
+ removed: true
184
+ });
185
+ }
186
+ if (line2) {
187
+ result.push({
188
+ value: line2,
189
+ added: true,
190
+ removed: false
191
+ });
192
+ }
193
+ }
194
+ }
195
+
196
+ return result;
197
+ }
198
+
199
+ // Navigation Functions
200
+ function viewContract(id) {
201
+ selectedContractId = id;
202
+ currentView = 'contract';
203
+ renderView();
204
+ }
205
+
206
+ function goBackToDashboard() {
207
+ selectedContractId = null;
208
+ currentView = 'dashboard';
209
+ renderView();
210
+ }
211
+
212
+ function goBackToContract() {
213
+ if (selectedContractId) {
214
+ currentView = 'contract';
215
+ } else {
216
+ goBackToDashboard();
217
+ }
218
+ renderView();
219
+ }
220
+
221
+ function goToAddVersion() {
222
+ currentView = 'addVersion';
223
+ renderView();
224
+ }
225
+
226
+ function goToCompareVersions(v1Id = null, v2Id = null) {
227
+ const contractVersions = versions[selectedContractId] || [];
228
+ const defaultV1 = v1Id ?? (contractVersions.length > 1 ? contractVersions[contractVersions.length - 2].id : null);
229
+ const defaultV2 = v2Id ?? (contractVersions.length > 0 ? contractVersions[contractVersions.length - 1].id : null);
230
+ compareVersionIds = { v1: defaultV1, v2: defaultV2 };
231
+ currentView = 'compare';
232
+ renderView();
233
+ }
234
+
235
+ function goToNewContract() {
236
+ currentView = 'newContract';
237
+ renderView();
238
+ }
239
+
240
+ // Data Manipulation Functions
241
+ function handleAddContract(name) {
242
+ const newId = Math.max(0, ...contracts.map(c => c.id)) + 1;
243
+ const newContract = {
244
+ id: newId,
245
+ name: name,
246
+ createdAt: new Date().toISOString(),
247
+ updatedAt: new Date().toISOString(),
248
+ };
249
+ contracts = [...contracts, newContract];
250
+ versions = { ...versions, [newId]: [] };
251
+
252
+ const initialVersion = {
253
+ id: Math.max(0, ...Object.values(versions).flat().map(v => v.id)) + 1,
254
+ contractId: newId,
255
+ content: `Initial content for ${name}. Please edit.`,
256
+ createdAt: new Date().toISOString(),
257
+ createdBy: "System",
258
+ };
259
+ versions = { ...versions, [newId]: [initialVersion] };
260
+
261
+ alert(`Contract '${name}' created successfully.`);
262
+ viewContract(newId);
263
+ }
264
+
265
+ function handleAddVersion(contractId, content, createdBy) {
266
+ if (!versions[contractId]) {
267
+ alert("Error: Contract not found.");
268
+ return;
269
+ }
270
+
271
+ const newVersionId = Math.max(0, ...Object.values(versions).flat().map(v => v.id)) + 1;
272
+ const newVersion = {
273
+ id: newVersionId,
274
+ contractId: contractId,
275
+ content: content,
276
+ createdAt: new Date().toISOString(),
277
+ createdBy: createdBy || "Unknown User",
278
+ };
279
+
280
+ versions = {
281
+ ...versions,
282
+ [contractId]: [...versions[contractId], newVersion],
283
+ };
284
+
285
+ contracts = contracts.map(c =>
286
+ c.id === contractId ? { ...c, updatedAt: new Date().toISOString() } : c
287
+ );
288
+
289
+ alert(`Version ${versions[contractId].length} added successfully.`);
290
+ goBackToContract();
291
+ }
292
+
293
+ function handleAddComment(versionId, comment, userId) {
294
+ const newCommentId = Math.max(0, ...Object.values(comments).flat().map(c => c.id)) + 1;
295
+ const newComment = {
296
+ id: newCommentId,
297
+ versionId: versionId,
298
+ userId: userId || "Guest",
299
+ comment: comment,
300
+ createdAt: new Date().toISOString(),
301
+ };
302
+
303
+ comments = {
304
+ ...comments,
305
+ [versionId]: [...(comments[versionId] || []), newComment],
306
+ };
307
+
308
+ renderView();
309
+ }
310
+
311
+ function handleFinalizeContract(contractId) {
312
+ const contract = contracts.find(c => c.id === contractId);
313
+ if (contract) {
314
+ alert(`Contract '${contract.name}' finalized successfully.`);
315
+ }
316
+ }
317
+
318
+ // View Components
319
+ function renderDashboard() {
320
+ return `
321
+ <div>
322
+ <div class="flex justify-between items-center mb-6">
323
+ <h2 class="text-3xl font-bold text-gray-800">Contracts Dashboard</h2>
324
+ <button
325
+ onclick="goToNewContract()"
326
+ class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition duration-200 shadow flex items-center"
327
+ >
328
+ <i class="fas fa-plus mr-2"></i> New Contract
329
+ </button>
330
+ </div>
331
+
332
+ ${contracts.length === 0 ? `
333
+ <div class="bg-white p-8 rounded-lg shadow text-center">
334
+ <i class="fas fa-file-alt text-4xl text-gray-400 mb-4"></i>
335
+ <p class="text-gray-500 mb-4">No contracts found. Create one to get started!</p>
336
+ <button
337
+ onclick="goToNewContract()"
338
+ class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition duration-200 shadow"
339
+ >
340
+ Create First Contract
341
+ </button>
342
+ </div>
343
+ ` : `
344
+ <div class="bg-white shadow overflow-hidden sm:rounded-md">
345
+ <ul class="divide-y divide-gray-200">
346
+ ${contracts.map(contract => `
347
+ <li key="${contract.id}" class="version-card">
348
+ <button
349
+ onclick="viewContract(${contract.id})"
350
+ class="block hover:bg-gray-50 w-full text-left px-4 py-4 sm:px-6 transition duration-150"
351
+ >
352
+ <div class="flex items-center justify-between">
353
+ <p class="text-lg font-medium text-blue-600 truncate">
354
+ <i class="fas fa-file-contract mr-2"></i>${contract.name}
355
+ </p>
356
+ <div class="ml-2 flex-shrink-0 flex">
357
+ <p class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">
358
+ Active
359
+ </p>
360
+ </div>
361
+ </div>
362
+ <div class="mt-2 sm:flex sm:justify-between">
363
+ <div class="sm:flex">
364
+ <p class="flex items-center text-sm text-gray-500">
365
+ <i class="fas fa-calendar-alt mr-2 text-gray-400"></i>
366
+ Created: ${formatDate(contract.createdAt)}
367
+ </p>
368
+ <p class="mt-2 flex items-center text-sm text-gray-500 sm:mt-0 sm:ml-6">
369
+ <i class="fas fa-clock mr-2 text-gray-400"></i>
370
+ Last Updated: ${formatDate(contract.updatedAt)}
371
+ </p>
372
+ </div>
373
+ <div class="mt-2 flex items-center text-sm text-gray-500 sm:mt-0">
374
+ <i class="fas fa-chevron-right text-gray-400"></i>
375
+ </div>
376
+ </div>
377
+ </button>
378
+ </li>
379
+ `).join('')}
380
+ </ul>
381
+ </div>
382
+ `}
383
+ </div>
384
+ `;
385
+ }
386
+
387
+ function renderNewContract() {
388
+ return `
389
+ <div class="bg-white p-6 rounded-lg shadow-md max-w-lg mx-auto">
390
+ <h2 class="text-2xl font-bold text-gray-800 mb-6">Create New Contract</h2>
391
+ <form onsubmit="event.preventDefault(); handleAddContract(document.getElementById('contractName').value);">
392
+ <div class="mb-4">
393
+ <label for="contractName" class="block text-sm font-medium text-gray-700 mb-1">
394
+ Contract Name
395
+ </label>
396
+ <input
397
+ type="text"
398
+ id="contractName"
399
+ class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
400
+ placeholder="e.g., Freelance Agreement - Alex & Client X"
401
+ required
402
+ >
403
+ </div>
404
+
405
+ <div class="mb-6">
406
+ <label for="contractFile" class="block text-sm font-medium text-gray-700 mb-1">
407
+ Upload Initial Document (Optional)
408
+ </label>
409
+ <div class="mt-1 flex justify-center px-6 pt-5 pb-6 border-2 border-gray-300 border-dashed rounded-md">
410
+ <div class="space-y-1 text-center">
411
+ <i class="fas fa-file-upload text-4xl text-gray-400 mx-auto"></i>
412
+ <div class="flex text-sm text-gray-600 justify-center">
413
+ <label class="relative cursor-pointer bg-white rounded-md font-medium text-blue-600 hover:text-blue-500">
414
+ <span>Upload a file</span>
415
+ <input id="contractFile" name="contractFile" type="file" class="sr-only">
416
+ </label>
417
+ <p class="pl-1">or drag and drop</p>
418
+ </div>
419
+ <p class="text-xs text-gray-500">PDF, DOC, DOCX, TXT up to 10MB</p>
420
+ </div>
421
+ </div>
422
+ </div>
423
+
424
+ <div class="flex justify-end space-x-3">
425
+ <button
426
+ type="button"
427
+ onclick="goBackToDashboard()"
428
+ class="bg-gray-200 text-gray-700 px-4 py-2 rounded-lg hover:bg-gray-300 transition duration-200"
429
+ >
430
+ Cancel
431
+ </button>
432
+ <button
433
+ type="submit"
434
+ class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition duration-200 shadow"
435
+ >
436
+ Create Contract
437
+ </button>
438
+ </div>
439
+ </form>
440
+ </div>
441
+ `;
442
+ }
443
+
444
+ function renderContractDetail() {
445
+ const contract = contracts.find(c => c.id === selectedContractId);
446
+ if (!contract) {
447
+ return `
448
+ <div class="bg-white p-6 rounded-lg shadow text-center">
449
+ <i class="fas fa-exclamation-triangle text-4xl text-yellow-500 mb-4"></i>
450
+ <p class="text-gray-700 mb-4">Contract not found.</p>
451
+ <button onclick="goBackToDashboard()" class="text-blue-600 hover:underline">
452
+ <i class="fas fa-arrow-left mr-2"></i>Go Back to Dashboard
453
+ </button>
454
+ </div>
455
+ `;
456
+ }
457
+
458
+ const contractVersions = versions[selectedContractId] || [];
459
+
460
+ return `
461
+ <div>
462
+ <button onclick="goBackToDashboard()" class="mb-6 text-blue-600 hover:underline flex items-center">
463
+ <i class="fas fa-arrow-left mr-2"></i> Back to Dashboard
464
+ </button>
465
+
466
+ <div class="flex justify-between items-start mb-6">
467
+ <div>
468
+ <h2 class="text-3xl font-bold text-gray-800 mb-1">${contract.name}</h2>
469
+ <p class="text-sm text-gray-500">
470
+ <i class="fas fa-clock mr-1"></i> Last Updated: ${formatDate(contract.updatedAt)}
471
+ </p>
472
+ </div>
473
+ <div class="flex space-x-2">
474
+ <button
475
+ onclick="handleFinalizeContract(${contract.id})"
476
+ class="bg-red-600 text-white px-3 py-1 rounded-lg hover:bg-red-700 transition duration-200 shadow text-sm"
477
+ title="Finalize Contract"
478
+ >
479
+ <i class="fas fa-lock mr-1"></i> Finalize
480
+ </button>
481
+ </div>
482
+ </div>
483
+
484
+ <div class="flex flex-wrap gap-3 mb-6">
485
+ <button
486
+ onclick="goToAddVersion()"
487
+ class="bg-green-600 text-white px-4 py-2 rounded-lg hover:bg-green-700 transition duration-200 shadow flex items-center"
488
+ >
489
+ <i class="fas fa-plus mr-2"></i> Add New Version
490
+ </button>
491
+ <button
492
+ onclick="goToCompareVersions()"
493
+ disabled="${contractVersions.length < 2}"
494
+ class="${contractVersions.length < 2 ? 'opacity-50 cursor-not-allowed' : ''} bg-yellow-500 text-white px-4 py-2 rounded-lg hover:bg-yellow-600 transition duration-200 shadow flex items-center"
495
+ >
496
+ <i class="fas fa-code-compare mr-2"></i> Compare Versions
497
+ </button>
498
+ </div>
499
+
500
+ <h3 class="text-2xl font-semibold text-gray-700 mb-4 flex items-center">
501
+ <i class="fas fa-history mr-2"></i> Version History
502
+ </h3>
503
+
504
+ ${contractVersions.length === 0 ? `
505
+ <div class="bg-white p-6 rounded-lg shadow text-center">
506
+ <i class="fas fa-file-alt text-4xl text-gray-400 mb-4"></i>
507
+ <p class="text-gray-500">No versions yet. Add the first version to get started!</p>
508
+ </div>
509
+ ` : `
510
+ <div class="space-y-4">
511
+ ${contractVersions.slice().reverse().map((version, index) => {
512
+ const versionNumber = contractVersions.length - index;
513
+ const versionComments = comments[version.id] || [];
514
+ const isCommenting = localStorage.getItem('commentingVersionId') === String(version.id);
515
+
516
+ return `
517
+ <div class="bg-white p-4 rounded-lg shadow border border-gray-200 version-card">
518
+ <div class="flex justify-between items-center mb-3">
519
+ <h4 class="text-lg font-semibold flex items-center">
520
+ <i class="fas fa-code-branch mr-2 text-blue-500"></i> Version ${versionNumber}
521
+ </h4>
522
+ <span class="text-sm text-gray-500">
523
+ By ${version.createdBy} at ${formatDate(version.createdAt)}
524
+ </span>
525
+ </div>
526
+
527
+ <div class="contract-content bg-gray-50 p-3 rounded mb-3">
528
+ <pre class="text-sm text-gray-700 whitespace-pre-wrap font-mono">${version.content}</pre>
529
+ </div>
530
+
531
+ <div class="mt-3 pt-3 border-t border-gray-200">
532
+ <h5 class="text-sm font-semibold text-gray-600 mb-2 flex items-center">
533
+ <i class="fas fa-comments mr-2"></i> Comments (${versionComments.length})
534
+ </h5>
535
+
536
+ ${versionComments.length > 0 ? `
537
+ <ul class="space-y-2 mb-3">
538
+ ${versionComments.map(comment => `
539
+ <li class="comment-bubble p-3">
540
+ <div class="flex justify-between items-start">
541
+ <span class="font-semibold text-sm">${comment.userId}:</span>
542
+ <span class="text-xs text-gray-400">${formatDate(comment.createdAt)}</span>
543
+ </div>
544
+ <p class="text-sm mt-1">${comment.comment}</p>
545
+ </li>
546
+ `).join('')}
547
+ </ul>
548
+ ` : ''}
549
+
550
+ ${isCommenting ? `
551
+ <form onsubmit="event.preventDefault(); handleAddComment(${version.id}, document.getElementById('comment-${version.id}').value, 'Alex'); localStorage.removeItem('commentingVersionId');">
552
+ <textarea
553
+ id="comment-${version.id}"
554
+ rows="2"
555
+ placeholder="Add a comment to Version ${versionNumber}..."
556
+ class="w-full p-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-1 focus:ring-blue-500"
557
+ required
558
+ ></textarea>
559
+ <div class="flex justify-end space-x-2 mt-1">
560
+ <button type="button" onclick="localStorage.removeItem('commentingVersionId'); renderView();" class="text-xs text-gray-500 hover:underline">Cancel</button>
561
+ <button type="submit" class="bg-blue-500 text-white px-2 py-1 rounded text-xs hover:bg-blue-600">Post</button>
562
+ </div>
563
+ </form>
564
+ ` : `
565
+ <button onclick="localStorage.setItem('commentingVersionId', '${version.id}'); renderView();" class="text-xs text-blue-600 hover:underline flex items-center">
566
+ <i class="fas fa-plus mr-1"></i> Add Comment
567
+ </button>
568
+ `}
569
+ </div>
570
+ </div>
571
+ `;
572
+ }).join('')}
573
+ </div>
574
+ `}
575
+ </div>
576
+ `;
577
+ }
578
+
579
+ function renderAddVersion() {
580
+ const baseVersion = versions[selectedContractId]?.[versions[selectedContractId].length - 1];
581
+
582
+ return `
583
+ <div class="bg-white p-6 rounded-lg shadow-md max-w-3xl mx-auto">
584
+ <button onclick="goBackToContract()" class="mb-4 text-sm text-blue-600 hover:underline flex items-center">
585
+ <i class="fas fa-arrow-left mr-1"></i> Cancel and Back to Contract
586
+ </button>
587
+
588
+ <h2 class="text-2xl font-bold text-gray-800 mb-4">Add New Version</h2>
589
+ <p class="text-sm text-gray-500 mb-4">
590
+ <i class="fas fa-info-circle mr-1"></i> Editing based on ${baseVersion ? `Version ID ${baseVersion.id}` : 'N/A'}
591
+ </p>
592
+
593
+ <form onsubmit="event.preventDefault(); handleAddVersion(${selectedContractId}, document.getElementById('versionContent').value, document.getElementById('createdBy').value);">
594
+ <div class="mb-4">
595
+ <label for="versionContent" class="block text-sm font-medium text-gray-700 mb-1">
596
+ Contract Content
597
+ </label>
598
+ <textarea
599
+ id="versionContent"
600
+ rows="15"
601
+ class="w-full p-3 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 font-mono text-sm"
602
+ placeholder="Enter contract text here..."
603
+ required
604
+ >${baseVersion?.content || ''}</textarea>
605
+ </div>
606
+
607
+ <div class="mb-6">
608
+ <label for="createdBy" class="block text-sm font-medium text-gray-700 mb-1">
609
+ Your Name (Editor)
610
+ </label>
611
+ <input
612
+ type="text"
613
+ id="createdBy"
614
+ value="Alex"
615
+ class="w-full sm:w-1/2 px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
616
+ required
617
+ >
618
+ </div>
619
+
620
+ <div class="flex justify-end space-x-3">
621
+ <button
622
+ type="button"
623
+ onclick="goBackToContract()"
624
+ class="bg-gray-200 text-gray-700 px-4 py-2 rounded-lg hover:bg-gray-300 transition duration-200"
625
+ >
626
+ Cancel
627
+ </button>
628
+ <button
629
+ type="submit"
630
+ class="bg-green-600 text-white px-4 py-2 rounded-lg hover:bg-green-700 transition duration-200 shadow"
631
+ >
632
+ <i class="fas fa-save mr-2"></i> Save New Version
633
+ </button>
634
+ </div>
635
+ </form>
636
+ </div>
637
+ `;
638
+ }
639
+
640
+ function renderCompareVersions() {
641
+ const contractVersions = versions[selectedContractId] || [];
642
+ const version1 = contractVersions.find(v => v.id === parseInt(compareVersionIds.v1));
643
+ const version2 = contractVersions.find(v => v.id === parseInt(compareVersionIds.v2));
644
+
645
+ const diffResult = version1 && version2 ? calculateDiff(version1.content, version2.content) : [];
646
+
647
+ return `
648
+ <div>
649
+ <button onclick="goBackToContract()" class="mb-6 text-blue-600 hover:underline flex items-center">
650
+ <i class="fas fa-arrow-left mr-2"></i> Back to Contract
651
+ </button>
652
+
653
+ <h2 class="text-3xl font-bold text-gray-800 mb-6 flex items-center">
654
+ <i class="fas fa-code-compare mr-3"></i> Compare Versions
655
+ </h2>
656
+
657
+ ${contractVersions.length < 2 ? `
658
+ <div class="bg-white p-6 rounded-lg shadow text-center">
659
+ <i class="fas fa-exclamation-circle text-4xl text-yellow-500 mb-4"></i>
660
+ <p class="text-gray-700 mb-4">You need at least two versions to compare.</p>
661
+ <button onclick="goToAddVersion()" class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition duration-200 shadow">
662
+ <i class="fas fa-plus mr-2"></i> Add Version
663
+ </button>
664
+ </div>
665
+ ` : `
666
+ <div class="flex space-x-4 mb-6 bg-white p-4 rounded-lg shadow border border-gray-200 items-center">
667
+ <div class="flex-1">
668
+ <label class="block text-sm font-medium text-gray-700 mb-1">Compare Version:</label>
669
+ <select
670
+ onchange="compareVersionIds.v1 = this.value; renderView();"
671
+ class="w-full p-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
672
+ value="${compareVersionIds.v1 || ''}"
673
+ >
674
+ <option value="" disabled>Select Version</option>
675
+ ${contractVersions.map((v, index) => `
676
+ <option value="${v.id}" ${v.id === parseInt(compareVersionIds.v1) ? 'selected' : ''}>
677
+ Version ${index + 1} (${formatDate(v.createdAt)})
678
+ </option>
679
+ `).join('')}
680
+ </select>
681
+ </div>
682
+ <span class="mt-6 text-gray-500">
683
+ <i class="fas fa-arrows-left-right"></i>
684
+ </span>
685
+ <div class="flex-1">
686
+ <label class="block text-sm font-medium text-gray-700 mb-1">With Version:</label>
687
+ <select
688
+ onchange="compareVersionIds.v2 = this.value; renderView();"
689
+ class="w-full p-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
690
+ value="${compareVersionIds.v2 || ''}"
691
+ >
692
+ <option value="" disabled>Select Version</option>
693
+ ${contractVersions.map((v, index) => `
694
+ <option value="${v.id}" ${v.id === parseInt(compareVersionIds.v2) ? 'selected' : ''}>
695
+ Version ${index + 1} (${formatDate(v.createdAt)})
696
+ </option>
697
+ `).join('')}
698
+ </select>
699
+ </div>
700
+ </div>
701
+
702
+ ${(version1 && version2) ? `
703
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4 font-mono text-sm bg-white rounded p-4 shadow border border-gray-300">
704
+ <div>
705
+ <h4 class="font-semibold mb-2 border-b pb-1">
706
+ Version ${contractVersions.findIndex(v => v.id === version1.id) + 1}
707
+ <span class="text-xs font-normal text-gray-500 ml-2">
708
+ (${formatDate(version1.createdAt)} by ${version1.createdBy})
709
+ </span>
710
+ </h4>
711
+ <pre class="whitespace-pre-wrap contract-content">${version1.content}</pre>
712
+ </div>
713
+ <div>
714
+ <h4 class="font-semibold mb-2 border-b pb-1">
715
+ Version ${contractVersions.findIndex(v => v.id === version2.id) + 1}
716
+ <span class="text-xs font-normal text-gray-500 ml-2">
717
+ (${formatDate(version2.createdAt)} by ${version2.createdBy})
718
+ </span>
719
+ </h4>
720
+ <pre class="whitespace-pre-wrap contract-content">${version2.content}</pre>
721
+ </div>
722
+ </div>
723
+
724
+ <div class="mt-8 bg-white p-4 rounded shadow border font-mono text-sm">
725
+ <h4 class="font-semibold mb-2 border-b pb-1">Changes Summary</h4>
726
+ <div class="whitespace-pre-wrap">
727
+ ${diffResult.map(part => {
728
+ if (part.added) {
729
+ return `<span class="diff-add">${part.value}</span>`;
730
+ } else if (part.removed) {
731
+ return `<span class="diff-del">${part.value}</span>`;
732
+ } else {
733
+ return `<span class="diff-neutral">${part.value}</span>`;
734
+ }
735
+ }).join('')}
736
+ </div>
737
+ </div>
738
+ ` : `
739
+ <div class="bg-white p-6 rounded-lg shadow text-center">
740
+ <i class="fas fa-info-circle text-4xl text-blue-500 mb-4"></i>
741
+ <p class="text-gray-700">Please select two versions to compare.</p>
742
+ </div>
743
+ `}
744
+ `}
745
+ </div>
746
+ `;
747
+ }
748
+
749
+ // Main Render Function
750
+ function renderView() {
751
+ let viewContent = '';
752
+
753
+ if (loading) {
754
+ viewContent = `
755
+ <div class="text-center p-10">
756
+ <i class="fas fa-spinner fa-spin text-4xl text-blue-500 mb-4"></i>
757
+ <p>Loading...</p>
758
+ </div>
759
+ `;
760
+ } else if (error) {
761
+ viewContent = `
762
+ <div class="text-center p-10 text-red-600">
763
+ <i class="fas fa-exclamation-triangle text-4xl mb-4"></i>
764
+ <p>${error}</p>
765
+ </div>
766
+ `;
767
+ } else {
768
+ switch (currentView) {
769
+ case 'contract':
770
+ viewContent = renderContractDetail();
771
+ break;
772
+ case 'compare':
773
+ viewContent = renderCompareVersions();
774
+ break;
775
+ case 'addVersion':
776
+ viewContent = renderAddVersion();
777
+ break;
778
+ case 'newContract':
779
+ viewContent = renderNewContract();
780
+ break;
781
+ case 'dashboard':
782
+ default:
783
+ viewContent = renderDashboard();
784
+ }
785
+ }
786
+
787
+ document.getElementById('dashboardView').innerHTML = viewContent;
788
+
789
+ // Update active button in header
790
+ document.getElementById('newContractBtn').classList.toggle('hidden', currentView !== 'dashboard');
791
+ }
792
+
793
+ // Initialize App
794
+ function initApp() {
795
+ // Simulate API Fetch
796
+ loading = true;
797
+ setTimeout(() => {
798
+ try {
799
+ contracts = MOCK_CONTRACTS;
800
+ versions = MOCK_VERSIONS;
801
+ comments = MOCK_COMMENTS;
802
+ loading = false;
803
+ renderView();
804
+ } catch (err) {
805
+ error = "Failed to load data.";
806
+ loading = false;
807
+ console.error(err);
808
+ renderView();
809
+ }
810
+ }, 500);
811
+
812
+ // Setup event listeners
813
+ document.getElementById('userMenuBtn').addEventListener('click', function() {
814
+ document.getElementById('userMenu').classList.toggle('hidden');
815
+ });
816
+
817
+ document.getElementById('newContractBtn').addEventListener('click', goToNewContract);
818
+
819
+ // Close user menu when clicking outside
820
+ document.addEventListener('click', function(event) {
821
+ if (!event.target.closest('#userMenu') && !event.target.closest('#userMenuBtn')) {
822
+ document.getElementById('userMenu').classList.add('hidden');
823
+ }
824
+ });
825
+ }
826
+
827
+ // Start the app when DOM is loaded
828
+ document.addEventListener('DOMContentLoaded', initApp);
829
+ </script>
830
+ <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=manideepreddym/contractverse" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
831
+ </html>