contractverse / index.html
manideepreddym's picture
Add 2 files
c4a61a5 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ContraGit App</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>
.diff-add {
background-color: #dcfce7;
color: #16a34a;
padding: 0 2px;
border-radius: 2px;
display: inline;
}
.diff-del {
background-color: #fee2e2;
color: #dc2626;
text-decoration: line-through;
padding: 0 2px;
border-radius: 2px;
display: inline;
}
.diff-neutral {
color: #4b5563;
display: inline;
}
pre {
white-space: pre-wrap;
word-wrap: break-word;
}
.contract-content {
max-height: 200px;
overflow-y: auto;
}
.version-card {
transition: all 0.2s ease;
}
.version-card:hover {
transform: translateY(-2px);
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
}
.comment-bubble {
position: relative;
background: #f3f4f6;
border-radius: 0.5rem;
}
.comment-bubble:after {
content: '';
position: absolute;
top: -10px;
left: 15px;
border-width: 0 10px 10px;
border-style: solid;
border-color: #f3f4f6 transparent;
}
</style>
</head>
<body class="min-h-screen bg-gray-100 font-sans">
<div id="app">
<!-- Header -->
<header class="bg-white shadow-sm">
<nav class="container mx-auto px-4 sm:px-6 lg:px-8 py-3 flex items-center justify-between">
<h1 class="text-2xl font-bold text-blue-600 cursor-pointer" onclick="goBackToDashboard()">
<i class="fas fa-file-contract mr-2"></i>ContraGit App
</h1>
<div class="flex items-center space-x-4">
<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">
<i class="fas fa-plus mr-2"></i> New Contract
</button>
<div class="relative">
<img src="https://randomuser.me/api/portraits/men/32.jpg" alt="User" class="w-8 h-8 rounded-full cursor-pointer" id="userMenuBtn">
<div id="userMenu" class="hidden absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg py-1 z-10">
<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>
</nav>
</header>
<!-- Main Content -->
<main class="container mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div id="dashboardView">
<!-- Dashboard content will be loaded here -->
</div>
</main>
<!-- Footer -->
<footer class="bg-gray-200 text-center py-4 mt-12">
<p class="text-sm text-gray-600">&copy; 2025 ContraGit App</p>
</footer>
</div>
<script>
// Mock Data
const MOCK_CONTRACTS = [
{
id: 1,
name: "Freelance Agreement - Alex & Client X",
createdAt: "2023-10-01T10:00:00Z",
updatedAt: "2023-10-01T11:00:00Z",
},
{
id: 2,
name: "NDA - Project Phoenix",
createdAt: "2023-09-15T14:30:00Z",
updatedAt: "2023-09-15T14:30:00Z",
},
];
const MOCK_VERSIONS = {
1: [
{ id: 1, contractId: 1, content: "Payment: $500 due on completion.\nDeadline: October 30th.", createdAt: "2023-10-01T10:00:00Z", createdBy: "Alex" },
{ 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" },
{ 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" },
],
2: [
{ id: 4, contractId: 2, content: "Standard Non-Disclosure Agreement terms apply.", createdAt: "2023-09-15T14:30:00Z", createdBy: "System" },
],
};
const MOCK_COMMENTS = {
2: [
{ 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" },
],
3: [
{ id: 2, versionId: 3, userId: "Alex", comment: "Deadline updated to Nov 5th. Please confirm.", createdAt: "2023-10-01T12:20:00Z" },
]
};
// State
let currentView = 'dashboard';
let selectedContractId = null;
let compareVersionIds = { v1: null, v2: null };
let contracts = [];
let versions = {};
let comments = {};
let loading = true;
let error = null;
// Utility Functions
function formatDate(isoString) {
if (!isoString) return 'N/A';
try {
const date = new Date(isoString);
return date.toLocaleString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
} catch (e) {
return 'Invalid Date';
}
}
function calculateDiff(text1, text2) {
// This is a simplified diff algorithm - in a real app, use a proper diff library
const lines1 = text1.split('\n');
const lines2 = text2.split('\n');
const maxLength = Math.max(lines1.length, lines2.length);
let result = [];
for (let i = 0; i < maxLength; i++) {
const line1 = lines1[i] || '';
const line2 = lines2[i] || '';
if (line1 === line2) {
result.push({
value: line1,
added: false,
removed: false
});
} else {
if (line1) {
result.push({
value: line1,
added: false,
removed: true
});
}
if (line2) {
result.push({
value: line2,
added: true,
removed: false
});
}
}
}
return result;
}
// Navigation Functions
function viewContract(id) {
selectedContractId = id;
currentView = 'contract';
renderView();
}
function goBackToDashboard() {
selectedContractId = null;
currentView = 'dashboard';
renderView();
}
function goBackToContract() {
if (selectedContractId) {
currentView = 'contract';
} else {
goBackToDashboard();
}
renderView();
}
function goToAddVersion() {
currentView = 'addVersion';
renderView();
}
function goToCompareVersions(v1Id = null, v2Id = null) {
const contractVersions = versions[selectedContractId] || [];
const defaultV1 = v1Id ?? (contractVersions.length > 1 ? contractVersions[contractVersions.length - 2].id : null);
const defaultV2 = v2Id ?? (contractVersions.length > 0 ? contractVersions[contractVersions.length - 1].id : null);
compareVersionIds = { v1: defaultV1, v2: defaultV2 };
currentView = 'compare';
renderView();
}
function goToNewContract() {
currentView = 'newContract';
renderView();
}
// Data Manipulation Functions
function handleAddContract(name) {
const newId = Math.max(0, ...contracts.map(c => c.id)) + 1;
const newContract = {
id: newId,
name: name,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
};
contracts = [...contracts, newContract];
versions = { ...versions, [newId]: [] };
const initialVersion = {
id: Math.max(0, ...Object.values(versions).flat().map(v => v.id)) + 1,
contractId: newId,
content: `Initial content for ${name}. Please edit.`,
createdAt: new Date().toISOString(),
createdBy: "System",
};
versions = { ...versions, [newId]: [initialVersion] };
alert(`Contract '${name}' created successfully.`);
viewContract(newId);
}
function handleAddVersion(contractId, content, createdBy) {
if (!versions[contractId]) {
alert("Error: Contract not found.");
return;
}
const newVersionId = Math.max(0, ...Object.values(versions).flat().map(v => v.id)) + 1;
const newVersion = {
id: newVersionId,
contractId: contractId,
content: content,
createdAt: new Date().toISOString(),
createdBy: createdBy || "Unknown User",
};
versions = {
...versions,
[contractId]: [...versions[contractId], newVersion],
};
contracts = contracts.map(c =>
c.id === contractId ? { ...c, updatedAt: new Date().toISOString() } : c
);
alert(`Version ${versions[contractId].length} added successfully.`);
goBackToContract();
}
function handleAddComment(versionId, comment, userId) {
const newCommentId = Math.max(0, ...Object.values(comments).flat().map(c => c.id)) + 1;
const newComment = {
id: newCommentId,
versionId: versionId,
userId: userId || "Guest",
comment: comment,
createdAt: new Date().toISOString(),
};
comments = {
...comments,
[versionId]: [...(comments[versionId] || []), newComment],
};
renderView();
}
function handleFinalizeContract(contractId) {
const contract = contracts.find(c => c.id === contractId);
if (contract) {
alert(`Contract '${contract.name}' finalized successfully.`);
}
}
// View Components
function renderDashboard() {
return `
<div>
<div class="flex justify-between items-center mb-6">
<h2 class="text-3xl font-bold text-gray-800">Contracts Dashboard</h2>
<button
onclick="goToNewContract()"
class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition duration-200 shadow flex items-center"
>
<i class="fas fa-plus mr-2"></i> New Contract
</button>
</div>
${contracts.length === 0 ? `
<div class="bg-white p-8 rounded-lg shadow text-center">
<i class="fas fa-file-alt text-4xl text-gray-400 mb-4"></i>
<p class="text-gray-500 mb-4">No contracts found. Create one to get started!</p>
<button
onclick="goToNewContract()"
class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition duration-200 shadow"
>
Create First Contract
</button>
</div>
` : `
<div class="bg-white shadow overflow-hidden sm:rounded-md">
<ul class="divide-y divide-gray-200">
${contracts.map(contract => `
<li key="${contract.id}" class="version-card">
<button
onclick="viewContract(${contract.id})"
class="block hover:bg-gray-50 w-full text-left px-4 py-4 sm:px-6 transition duration-150"
>
<div class="flex items-center justify-between">
<p class="text-lg font-medium text-blue-600 truncate">
<i class="fas fa-file-contract mr-2"></i>${contract.name}
</p>
<div class="ml-2 flex-shrink-0 flex">
<p class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">
Active
</p>
</div>
</div>
<div class="mt-2 sm:flex sm:justify-between">
<div class="sm:flex">
<p class="flex items-center text-sm text-gray-500">
<i class="fas fa-calendar-alt mr-2 text-gray-400"></i>
Created: ${formatDate(contract.createdAt)}
</p>
<p class="mt-2 flex items-center text-sm text-gray-500 sm:mt-0 sm:ml-6">
<i class="fas fa-clock mr-2 text-gray-400"></i>
Last Updated: ${formatDate(contract.updatedAt)}
</p>
</div>
<div class="mt-2 flex items-center text-sm text-gray-500 sm:mt-0">
<i class="fas fa-chevron-right text-gray-400"></i>
</div>
</div>
</button>
</li>
`).join('')}
</ul>
</div>
`}
</div>
`;
}
function renderNewContract() {
return `
<div class="bg-white p-6 rounded-lg shadow-md max-w-lg mx-auto">
<h2 class="text-2xl font-bold text-gray-800 mb-6">Create New Contract</h2>
<form onsubmit="event.preventDefault(); handleAddContract(document.getElementById('contractName').value);">
<div class="mb-4">
<label for="contractName" class="block text-sm font-medium text-gray-700 mb-1">
Contract Name
</label>
<input
type="text"
id="contractName"
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"
placeholder="e.g., Freelance Agreement - Alex & Client X"
required
>
</div>
<div class="mb-6">
<label for="contractFile" class="block text-sm font-medium text-gray-700 mb-1">
Upload Initial Document (Optional)
</label>
<div class="mt-1 flex justify-center px-6 pt-5 pb-6 border-2 border-gray-300 border-dashed rounded-md">
<div class="space-y-1 text-center">
<i class="fas fa-file-upload text-4xl text-gray-400 mx-auto"></i>
<div class="flex text-sm text-gray-600 justify-center">
<label class="relative cursor-pointer bg-white rounded-md font-medium text-blue-600 hover:text-blue-500">
<span>Upload a file</span>
<input id="contractFile" name="contractFile" type="file" class="sr-only">
</label>
<p class="pl-1">or drag and drop</p>
</div>
<p class="text-xs text-gray-500">PDF, DOC, DOCX, TXT up to 10MB</p>
</div>
</div>
</div>
<div class="flex justify-end space-x-3">
<button
type="button"
onclick="goBackToDashboard()"
class="bg-gray-200 text-gray-700 px-4 py-2 rounded-lg hover:bg-gray-300 transition duration-200"
>
Cancel
</button>
<button
type="submit"
class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition duration-200 shadow"
>
Create Contract
</button>
</div>
</form>
</div>
`;
}
function renderContractDetail() {
const contract = contracts.find(c => c.id === selectedContractId);
if (!contract) {
return `
<div class="bg-white p-6 rounded-lg shadow text-center">
<i class="fas fa-exclamation-triangle text-4xl text-yellow-500 mb-4"></i>
<p class="text-gray-700 mb-4">Contract not found.</p>
<button onclick="goBackToDashboard()" class="text-blue-600 hover:underline">
<i class="fas fa-arrow-left mr-2"></i>Go Back to Dashboard
</button>
</div>
`;
}
const contractVersions = versions[selectedContractId] || [];
return `
<div>
<button onclick="goBackToDashboard()" class="mb-6 text-blue-600 hover:underline flex items-center">
<i class="fas fa-arrow-left mr-2"></i> Back to Dashboard
</button>
<div class="flex justify-between items-start mb-6">
<div>
<h2 class="text-3xl font-bold text-gray-800 mb-1">${contract.name}</h2>
<p class="text-sm text-gray-500">
<i class="fas fa-clock mr-1"></i> Last Updated: ${formatDate(contract.updatedAt)}
</p>
</div>
<div class="flex space-x-2">
<button
onclick="handleFinalizeContract(${contract.id})"
class="bg-red-600 text-white px-3 py-1 rounded-lg hover:bg-red-700 transition duration-200 shadow text-sm"
title="Finalize Contract"
>
<i class="fas fa-lock mr-1"></i> Finalize
</button>
</div>
</div>
<div class="flex flex-wrap gap-3 mb-6">
<button
onclick="goToAddVersion()"
class="bg-green-600 text-white px-4 py-2 rounded-lg hover:bg-green-700 transition duration-200 shadow flex items-center"
>
<i class="fas fa-plus mr-2"></i> Add New Version
</button>
<button
onclick="goToCompareVersions()"
disabled="${contractVersions.length < 2}"
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"
>
<i class="fas fa-code-compare mr-2"></i> Compare Versions
</button>
</div>
<h3 class="text-2xl font-semibold text-gray-700 mb-4 flex items-center">
<i class="fas fa-history mr-2"></i> Version History
</h3>
${contractVersions.length === 0 ? `
<div class="bg-white p-6 rounded-lg shadow text-center">
<i class="fas fa-file-alt text-4xl text-gray-400 mb-4"></i>
<p class="text-gray-500">No versions yet. Add the first version to get started!</p>
</div>
` : `
<div class="space-y-4">
${contractVersions.slice().reverse().map((version, index) => {
const versionNumber = contractVersions.length - index;
const versionComments = comments[version.id] || [];
const isCommenting = localStorage.getItem('commentingVersionId') === String(version.id);
return `
<div class="bg-white p-4 rounded-lg shadow border border-gray-200 version-card">
<div class="flex justify-between items-center mb-3">
<h4 class="text-lg font-semibold flex items-center">
<i class="fas fa-code-branch mr-2 text-blue-500"></i> Version ${versionNumber}
</h4>
<span class="text-sm text-gray-500">
By ${version.createdBy} at ${formatDate(version.createdAt)}
</span>
</div>
<div class="contract-content bg-gray-50 p-3 rounded mb-3">
<pre class="text-sm text-gray-700 whitespace-pre-wrap font-mono">${version.content}</pre>
</div>
<div class="mt-3 pt-3 border-t border-gray-200">
<h5 class="text-sm font-semibold text-gray-600 mb-2 flex items-center">
<i class="fas fa-comments mr-2"></i> Comments (${versionComments.length})
</h5>
${versionComments.length > 0 ? `
<ul class="space-y-2 mb-3">
${versionComments.map(comment => `
<li class="comment-bubble p-3">
<div class="flex justify-between items-start">
<span class="font-semibold text-sm">${comment.userId}:</span>
<span class="text-xs text-gray-400">${formatDate(comment.createdAt)}</span>
</div>
<p class="text-sm mt-1">${comment.comment}</p>
</li>
`).join('')}
</ul>
` : ''}
${isCommenting ? `
<form onsubmit="event.preventDefault(); handleAddComment(${version.id}, document.getElementById('comment-${version.id}').value, 'Alex'); localStorage.removeItem('commentingVersionId');">
<textarea
id="comment-${version.id}"
rows="2"
placeholder="Add a comment to Version ${versionNumber}..."
class="w-full p-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-1 focus:ring-blue-500"
required
></textarea>
<div class="flex justify-end space-x-2 mt-1">
<button type="button" onclick="localStorage.removeItem('commentingVersionId'); renderView();" class="text-xs text-gray-500 hover:underline">Cancel</button>
<button type="submit" class="bg-blue-500 text-white px-2 py-1 rounded text-xs hover:bg-blue-600">Post</button>
</div>
</form>
` : `
<button onclick="localStorage.setItem('commentingVersionId', '${version.id}'); renderView();" class="text-xs text-blue-600 hover:underline flex items-center">
<i class="fas fa-plus mr-1"></i> Add Comment
</button>
`}
</div>
</div>
`;
}).join('')}
</div>
`}
</div>
`;
}
function renderAddVersion() {
const baseVersion = versions[selectedContractId]?.[versions[selectedContractId].length - 1];
return `
<div class="bg-white p-6 rounded-lg shadow-md max-w-3xl mx-auto">
<button onclick="goBackToContract()" class="mb-4 text-sm text-blue-600 hover:underline flex items-center">
<i class="fas fa-arrow-left mr-1"></i> Cancel and Back to Contract
</button>
<h2 class="text-2xl font-bold text-gray-800 mb-4">Add New Version</h2>
<p class="text-sm text-gray-500 mb-4">
<i class="fas fa-info-circle mr-1"></i> Editing based on ${baseVersion ? `Version ID ${baseVersion.id}` : 'N/A'}
</p>
<form onsubmit="event.preventDefault(); handleAddVersion(${selectedContractId}, document.getElementById('versionContent').value, document.getElementById('createdBy').value);">
<div class="mb-4">
<label for="versionContent" class="block text-sm font-medium text-gray-700 mb-1">
Contract Content
</label>
<textarea
id="versionContent"
rows="15"
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"
placeholder="Enter contract text here..."
required
>${baseVersion?.content || ''}</textarea>
</div>
<div class="mb-6">
<label for="createdBy" class="block text-sm font-medium text-gray-700 mb-1">
Your Name (Editor)
</label>
<input
type="text"
id="createdBy"
value="Alex"
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"
required
>
</div>
<div class="flex justify-end space-x-3">
<button
type="button"
onclick="goBackToContract()"
class="bg-gray-200 text-gray-700 px-4 py-2 rounded-lg hover:bg-gray-300 transition duration-200"
>
Cancel
</button>
<button
type="submit"
class="bg-green-600 text-white px-4 py-2 rounded-lg hover:bg-green-700 transition duration-200 shadow"
>
<i class="fas fa-save mr-2"></i> Save New Version
</button>
</div>
</form>
</div>
`;
}
function renderCompareVersions() {
const contractVersions = versions[selectedContractId] || [];
const version1 = contractVersions.find(v => v.id === parseInt(compareVersionIds.v1));
const version2 = contractVersions.find(v => v.id === parseInt(compareVersionIds.v2));
const diffResult = version1 && version2 ? calculateDiff(version1.content, version2.content) : [];
return `
<div>
<button onclick="goBackToContract()" class="mb-6 text-blue-600 hover:underline flex items-center">
<i class="fas fa-arrow-left mr-2"></i> Back to Contract
</button>
<h2 class="text-3xl font-bold text-gray-800 mb-6 flex items-center">
<i class="fas fa-code-compare mr-3"></i> Compare Versions
</h2>
${contractVersions.length < 2 ? `
<div class="bg-white p-6 rounded-lg shadow text-center">
<i class="fas fa-exclamation-circle text-4xl text-yellow-500 mb-4"></i>
<p class="text-gray-700 mb-4">You need at least two versions to compare.</p>
<button onclick="goToAddVersion()" class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition duration-200 shadow">
<i class="fas fa-plus mr-2"></i> Add Version
</button>
</div>
` : `
<div class="flex space-x-4 mb-6 bg-white p-4 rounded-lg shadow border border-gray-200 items-center">
<div class="flex-1">
<label class="block text-sm font-medium text-gray-700 mb-1">Compare Version:</label>
<select
onchange="compareVersionIds.v1 = this.value; renderView();"
class="w-full p-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
value="${compareVersionIds.v1 || ''}"
>
<option value="" disabled>Select Version</option>
${contractVersions.map((v, index) => `
<option value="${v.id}" ${v.id === parseInt(compareVersionIds.v1) ? 'selected' : ''}>
Version ${index + 1} (${formatDate(v.createdAt)})
</option>
`).join('')}
</select>
</div>
<span class="mt-6 text-gray-500">
<i class="fas fa-arrows-left-right"></i>
</span>
<div class="flex-1">
<label class="block text-sm font-medium text-gray-700 mb-1">With Version:</label>
<select
onchange="compareVersionIds.v2 = this.value; renderView();"
class="w-full p-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
value="${compareVersionIds.v2 || ''}"
>
<option value="" disabled>Select Version</option>
${contractVersions.map((v, index) => `
<option value="${v.id}" ${v.id === parseInt(compareVersionIds.v2) ? 'selected' : ''}>
Version ${index + 1} (${formatDate(v.createdAt)})
</option>
`).join('')}
</select>
</div>
</div>
${(version1 && version2) ? `
<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">
<div>
<h4 class="font-semibold mb-2 border-b pb-1">
Version ${contractVersions.findIndex(v => v.id === version1.id) + 1}
<span class="text-xs font-normal text-gray-500 ml-2">
(${formatDate(version1.createdAt)} by ${version1.createdBy})
</span>
</h4>
<pre class="whitespace-pre-wrap contract-content">${version1.content}</pre>
</div>
<div>
<h4 class="font-semibold mb-2 border-b pb-1">
Version ${contractVersions.findIndex(v => v.id === version2.id) + 1}
<span class="text-xs font-normal text-gray-500 ml-2">
(${formatDate(version2.createdAt)} by ${version2.createdBy})
</span>
</h4>
<pre class="whitespace-pre-wrap contract-content">${version2.content}</pre>
</div>
</div>
<div class="mt-8 bg-white p-4 rounded shadow border font-mono text-sm">
<h4 class="font-semibold mb-2 border-b pb-1">Changes Summary</h4>
<div class="whitespace-pre-wrap">
${diffResult.map(part => {
if (part.added) {
return `<span class="diff-add">${part.value}</span>`;
} else if (part.removed) {
return `<span class="diff-del">${part.value}</span>`;
} else {
return `<span class="diff-neutral">${part.value}</span>`;
}
}).join('')}
</div>
</div>
` : `
<div class="bg-white p-6 rounded-lg shadow text-center">
<i class="fas fa-info-circle text-4xl text-blue-500 mb-4"></i>
<p class="text-gray-700">Please select two versions to compare.</p>
</div>
`}
`}
</div>
`;
}
// Main Render Function
function renderView() {
let viewContent = '';
if (loading) {
viewContent = `
<div class="text-center p-10">
<i class="fas fa-spinner fa-spin text-4xl text-blue-500 mb-4"></i>
<p>Loading...</p>
</div>
`;
} else if (error) {
viewContent = `
<div class="text-center p-10 text-red-600">
<i class="fas fa-exclamation-triangle text-4xl mb-4"></i>
<p>${error}</p>
</div>
`;
} else {
switch (currentView) {
case 'contract':
viewContent = renderContractDetail();
break;
case 'compare':
viewContent = renderCompareVersions();
break;
case 'addVersion':
viewContent = renderAddVersion();
break;
case 'newContract':
viewContent = renderNewContract();
break;
case 'dashboard':
default:
viewContent = renderDashboard();
}
}
document.getElementById('dashboardView').innerHTML = viewContent;
// Update active button in header
document.getElementById('newContractBtn').classList.toggle('hidden', currentView !== 'dashboard');
}
// Initialize App
function initApp() {
// Simulate API Fetch
loading = true;
setTimeout(() => {
try {
contracts = MOCK_CONTRACTS;
versions = MOCK_VERSIONS;
comments = MOCK_COMMENTS;
loading = false;
renderView();
} catch (err) {
error = "Failed to load data.";
loading = false;
console.error(err);
renderView();
}
}, 500);
// Setup event listeners
document.getElementById('userMenuBtn').addEventListener('click', function() {
document.getElementById('userMenu').classList.toggle('hidden');
});
document.getElementById('newContractBtn').addEventListener('click', goToNewContract);
// Close user menu when clicking outside
document.addEventListener('click', function(event) {
if (!event.target.closest('#userMenu') && !event.target.closest('#userMenuBtn')) {
document.getElementById('userMenu').classList.add('hidden');
}
});
}
// Start the app when DOM is loaded
document.addEventListener('DOMContentLoaded', initApp);
</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=manideepreddym/contractverse" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>