feat: Goa GEL Blockchain e-Licensing Platform - Full Stack Implementation
Complete implementation of the Goa Government e-Licensing platform with: Backend: - NestJS API with JWT authentication - PostgreSQL database with Knex ORM - Redis caching and session management - MinIO document storage - Hyperledger Besu blockchain integration - Multi-department workflow system - Comprehensive API tests (266/282 passing) Frontend: - Angular 21 with standalone components - Angular Material + TailwindCSS UI - Visual workflow builder - Document upload with progress tracking - Blockchain explorer integration - Role-based dashboards (Admin, Department, Citizen) - E2E tests with Playwright (37 tests) Infrastructure: - Docker Compose orchestration - Blockscout blockchain explorer - Development and production configurations
This commit is contained in:
22
Documentation/public/404.html
Normal file
22
Documentation/public/404.html
Normal file
@@ -0,0 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>404 - Page Not Found | Goa-GEL Documentation</title>
|
||||
<link rel="stylesheet" href="/css/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container" style="text-align: center; padding: 100px 20px;">
|
||||
<h1 style="font-size: 6rem; margin-bottom: 20px;">404</h1>
|
||||
<h2 style="margin-bottom: 20px;">Page Not Found</h2>
|
||||
<p style="color: #666; margin-bottom: 40px;">
|
||||
The page you're looking for doesn't exist or has been moved.
|
||||
</p>
|
||||
<div style="display: flex; gap: 20px; justify-content: center; flex-wrap: wrap;">
|
||||
<a href="/" class="btn btn-primary">Go to Homepage</a>
|
||||
<a href="/viewer.html?doc=DOCUMENTATION_INDEX" class="btn btn-secondary">View Documentation</a>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
772
Documentation/public/css/styles.css
Normal file
772
Documentation/public/css/styles.css
Normal file
@@ -0,0 +1,772 @@
|
||||
/* Global Styles */
|
||||
:root {
|
||||
--primary-color: #1976d2;
|
||||
--secondary-color: #424242;
|
||||
--success-color: #4caf50;
|
||||
--warning-color: #ff9800;
|
||||
--error-color: #f44336;
|
||||
--info-color: #2196f3;
|
||||
|
||||
--bg-primary: #ffffff;
|
||||
--bg-secondary: #f5f5f5;
|
||||
--bg-dark: #1a1a1a;
|
||||
|
||||
--text-primary: #212121;
|
||||
--text-secondary: #757575;
|
||||
--text-light: #ffffff;
|
||||
|
||||
--border-color: #e0e0e0;
|
||||
--shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
--shadow-lg: 0 4px 16px rgba(0, 0, 0, 0.15);
|
||||
|
||||
--spacing-xs: 0.5rem;
|
||||
--spacing-sm: 1rem;
|
||||
--spacing-md: 1.5rem;
|
||||
--spacing-lg: 2rem;
|
||||
--spacing-xl: 3rem;
|
||||
|
||||
--border-radius: 8px;
|
||||
--transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
|
||||
line-height: 1.6;
|
||||
color: var(--text-primary);
|
||||
background-color: var(--bg-secondary);
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 var(--spacing-md);
|
||||
}
|
||||
|
||||
/* Header */
|
||||
.header {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: var(--text-light);
|
||||
padding: var(--spacing-md) 0;
|
||||
box-shadow: var(--shadow-lg);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.header-content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.logo h1 {
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.logo p {
|
||||
font-size: 0.875rem;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.logo a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.main-nav {
|
||||
display: flex;
|
||||
gap: var(--spacing-md);
|
||||
}
|
||||
|
||||
.main-nav a {
|
||||
color: var(--text-light);
|
||||
text-decoration: none;
|
||||
padding: var(--spacing-xs) var(--spacing-sm);
|
||||
border-radius: var(--border-radius);
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.main-nav a:hover,
|
||||
.main-nav a.active {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
/* Hero Section */
|
||||
.hero {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: var(--text-light);
|
||||
padding: var(--spacing-xl) 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.hero-title {
|
||||
font-size: 3rem;
|
||||
margin-bottom: var(--spacing-sm);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.hero-subtitle {
|
||||
font-size: 1.25rem;
|
||||
margin-bottom: var(--spacing-lg);
|
||||
opacity: 0.95;
|
||||
}
|
||||
|
||||
.hero-stats {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: var(--spacing-xl);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.stat {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 0.875rem;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
/* Quick Start Section */
|
||||
.quick-start {
|
||||
padding: var(--spacing-xl) 0;
|
||||
background-color: var(--bg-primary);
|
||||
}
|
||||
|
||||
.quick-start h2 {
|
||||
text-align: center;
|
||||
font-size: 2rem;
|
||||
margin-bottom: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.cards-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||
gap: var(--spacing-md);
|
||||
}
|
||||
|
||||
.card {
|
||||
background: var(--bg-primary);
|
||||
border: 2px solid var(--border-color);
|
||||
border-radius: var(--border-radius);
|
||||
padding: var(--spacing-lg);
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
transition: var(--transition);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: var(--shadow-lg);
|
||||
}
|
||||
|
||||
.card-blue { border-left: 4px solid #2196f3; }
|
||||
.card-green { border-left: 4px solid #4caf50; }
|
||||
.card-purple { border-left: 4px solid #9c27b0; }
|
||||
.card-orange { border-left: 4px solid #ff9800; }
|
||||
|
||||
.card-icon {
|
||||
font-size: 3rem;
|
||||
margin-bottom: var(--spacing-sm);
|
||||
}
|
||||
|
||||
.card h3 {
|
||||
font-size: 1.25rem;
|
||||
margin-bottom: var(--spacing-xs);
|
||||
}
|
||||
|
||||
.card p {
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: var(--spacing-sm);
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.card-button {
|
||||
color: var(--primary-color);
|
||||
font-weight: 600;
|
||||
display: inline-block;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
/* Documentation Section */
|
||||
.documentation {
|
||||
padding: var(--spacing-xl) 0;
|
||||
background-color: var(--bg-secondary);
|
||||
}
|
||||
|
||||
.documentation h2 {
|
||||
text-align: center;
|
||||
font-size: 2rem;
|
||||
margin-bottom: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.doc-category {
|
||||
background: var(--bg-primary);
|
||||
border-radius: var(--border-radius);
|
||||
padding: var(--spacing-lg);
|
||||
margin-bottom: var(--spacing-lg);
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.doc-category h3 {
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: var(--spacing-md);
|
||||
padding-bottom: var(--spacing-sm);
|
||||
border-bottom: 2px solid var(--border-color);
|
||||
}
|
||||
|
||||
.doc-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-md);
|
||||
}
|
||||
|
||||
.doc-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: var(--spacing-md);
|
||||
background: var(--bg-secondary);
|
||||
border-radius: var(--border-radius);
|
||||
gap: var(--spacing-md);
|
||||
}
|
||||
|
||||
.doc-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.doc-item h4 {
|
||||
font-size: 1.125rem;
|
||||
margin-bottom: var(--spacing-xs);
|
||||
}
|
||||
|
||||
.doc-item p {
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: var(--spacing-xs);
|
||||
}
|
||||
|
||||
.doc-meta {
|
||||
display: flex;
|
||||
gap: var(--spacing-xs);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 0.25rem 0.75rem;
|
||||
background-color: var(--primary-color);
|
||||
color: var(--text-light);
|
||||
border-radius: 20px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.btn {
|
||||
display: inline-block;
|
||||
padding: 0.75rem 1.5rem;
|
||||
border: none;
|
||||
border-radius: var(--border-radius);
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: var(--primary-color);
|
||||
color: var(--text-light);
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background-color: #1565c0;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background-color: var(--secondary-color);
|
||||
color: var(--text-light);
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background-color: #303030;
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
padding: 0.5rem 1rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
/* Features Section */
|
||||
.features {
|
||||
padding: var(--spacing-xl) 0;
|
||||
background-color: var(--bg-primary);
|
||||
}
|
||||
|
||||
.features h2 {
|
||||
text-align: center;
|
||||
font-size: 2rem;
|
||||
margin-bottom: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.features-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.feature {
|
||||
text-align: center;
|
||||
padding: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.feature-icon {
|
||||
font-size: 3rem;
|
||||
margin-bottom: var(--spacing-sm);
|
||||
}
|
||||
|
||||
.feature h3 {
|
||||
font-size: 1.125rem;
|
||||
margin-bottom: var(--spacing-xs);
|
||||
}
|
||||
|
||||
.feature p {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
/* Roles Section */
|
||||
.roles {
|
||||
padding: var(--spacing-xl) 0;
|
||||
background-color: var(--bg-secondary);
|
||||
}
|
||||
|
||||
.roles h2 {
|
||||
text-align: center;
|
||||
font-size: 2rem;
|
||||
margin-bottom: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.roles-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||
gap: var(--spacing-md);
|
||||
}
|
||||
|
||||
.role-card {
|
||||
background: var(--bg-primary);
|
||||
border-radius: var(--border-radius);
|
||||
padding: var(--spacing-lg);
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.role-card h3 {
|
||||
font-size: 1.25rem;
|
||||
margin-bottom: var(--spacing-sm);
|
||||
padding-bottom: var(--spacing-sm);
|
||||
border-bottom: 2px solid var(--border-color);
|
||||
}
|
||||
|
||||
.role-card ul {
|
||||
list-style: none;
|
||||
margin-bottom: var(--spacing-md);
|
||||
}
|
||||
|
||||
.role-card li {
|
||||
padding: var(--spacing-xs) 0;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.role-card li::before {
|
||||
content: "✓ ";
|
||||
color: var(--success-color);
|
||||
font-weight: bold;
|
||||
margin-right: var(--spacing-xs);
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
.footer {
|
||||
background-color: var(--bg-dark);
|
||||
color: var(--text-light);
|
||||
padding: var(--spacing-xl) 0 var(--spacing-md);
|
||||
}
|
||||
|
||||
.footer-content {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: var(--spacing-lg);
|
||||
margin-bottom: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.footer-section h4 {
|
||||
margin-bottom: var(--spacing-sm);
|
||||
}
|
||||
|
||||
.footer-section p,
|
||||
.footer-section a {
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
margin-bottom: var(--spacing-xs);
|
||||
}
|
||||
|
||||
.footer-section a:hover {
|
||||
color: var(--text-light);
|
||||
}
|
||||
|
||||
.footer-bottom {
|
||||
text-align: center;
|
||||
padding-top: var(--spacing-md);
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
/* Viewer Layout */
|
||||
.viewer-container {
|
||||
display: flex;
|
||||
min-height: calc(100vh - 80px);
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: 280px;
|
||||
background: var(--bg-primary);
|
||||
border-right: 1px solid var(--border-color);
|
||||
position: sticky;
|
||||
top: 80px;
|
||||
height: calc(100vh - 80px);
|
||||
overflow-y: auto;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
padding: var(--spacing-md);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.sidebar-nav {
|
||||
padding: var(--spacing-md);
|
||||
}
|
||||
|
||||
.nav-section {
|
||||
margin-bottom: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.nav-section h4 {
|
||||
font-size: 0.875rem;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
margin-bottom: var(--spacing-sm);
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-xs);
|
||||
padding: var(--spacing-xs) var(--spacing-sm);
|
||||
color: var(--text-primary);
|
||||
text-decoration: none;
|
||||
border-radius: var(--border-radius);
|
||||
transition: var(--transition);
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.nav-link:hover,
|
||||
.nav-link.active {
|
||||
background-color: var(--bg-secondary);
|
||||
}
|
||||
|
||||
.nav-icon {
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
|
||||
.viewer-main {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.viewer-toolbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: var(--spacing-sm) var(--spacing-md);
|
||||
background: var(--bg-primary);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
position: sticky;
|
||||
top: 80px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.toolbar-left,
|
||||
.toolbar-right {
|
||||
display: flex;
|
||||
gap: var(--spacing-sm);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.doc-selector {
|
||||
padding: 0.5rem;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--border-radius);
|
||||
background: var(--bg-primary);
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.document-content {
|
||||
flex: 1;
|
||||
padding: var(--spacing-lg);
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Markdown Styles */
|
||||
.markdown-body {
|
||||
font-size: 1rem;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.markdown-body h1,
|
||||
.markdown-body h2,
|
||||
.markdown-body h3,
|
||||
.markdown-body h4 {
|
||||
margin-top: var(--spacing-lg);
|
||||
margin-bottom: var(--spacing-sm);
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.markdown-body h1 { font-size: 2.5rem; }
|
||||
.markdown-body h2 { font-size: 2rem; border-bottom: 2px solid var(--border-color); padding-bottom: var(--spacing-xs); }
|
||||
.markdown-body h3 { font-size: 1.5rem; }
|
||||
.markdown-body h4 { font-size: 1.25rem; }
|
||||
|
||||
.markdown-body p {
|
||||
margin-bottom: var(--spacing-sm);
|
||||
}
|
||||
|
||||
.markdown-body ul,
|
||||
.markdown-body ol {
|
||||
margin-bottom: var(--spacing-sm);
|
||||
padding-left: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.markdown-body li {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.markdown-body code {
|
||||
background-color: var(--bg-secondary);
|
||||
padding: 0.2rem 0.4rem;
|
||||
border-radius: 4px;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.markdown-body pre {
|
||||
background-color: var(--bg-dark);
|
||||
color: var(--text-light);
|
||||
padding: var(--spacing-md);
|
||||
border-radius: var(--border-radius);
|
||||
overflow-x: auto;
|
||||
margin-bottom: var(--spacing-md);
|
||||
}
|
||||
|
||||
.markdown-body pre code {
|
||||
background: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.markdown-body blockquote {
|
||||
border-left: 4px solid var(--primary-color);
|
||||
padding-left: var(--spacing-md);
|
||||
margin: var(--spacing-md) 0;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.markdown-body table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-bottom: var(--spacing-md);
|
||||
}
|
||||
|
||||
.markdown-body th,
|
||||
.markdown-body td {
|
||||
border: 1px solid var(--border-color);
|
||||
padding: var(--spacing-xs) var(--spacing-sm);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.markdown-body th {
|
||||
background-color: var(--bg-secondary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown-body a {
|
||||
color: var(--primary-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.markdown-body a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.markdown-body img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
border-radius: var(--border-radius);
|
||||
margin: var(--spacing-md) 0;
|
||||
}
|
||||
|
||||
/* Table of Contents */
|
||||
.toc {
|
||||
position: fixed;
|
||||
right: var(--spacing-md);
|
||||
top: 200px;
|
||||
width: 250px;
|
||||
max-height: calc(100vh - 250px);
|
||||
overflow-y: auto;
|
||||
background: var(--bg-primary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--border-radius);
|
||||
padding: var(--spacing-md);
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.toc h4 {
|
||||
margin-bottom: var(--spacing-sm);
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
#toc-content {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
#toc-content a {
|
||||
display: block;
|
||||
padding: 0.25rem 0;
|
||||
color: var(--text-secondary);
|
||||
text-decoration: none;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
#toc-content a:hover {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
/* Loading & Error States */
|
||||
.loading {
|
||||
text-align: center;
|
||||
padding: var(--spacing-xl);
|
||||
}
|
||||
|
||||
.spinner {
|
||||
border: 4px solid var(--border-color);
|
||||
border-top: 4px solid var(--primary-color);
|
||||
border-radius: 50%;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
animation: spin 1s linear infinite;
|
||||
margin: 0 auto var(--spacing-md);
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.error {
|
||||
text-align: center;
|
||||
padding: var(--spacing-xl);
|
||||
color: var(--error-color);
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (max-width: 1200px) {
|
||||
.toc {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.hero-title {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
left: -280px;
|
||||
transition: left 0.3s ease;
|
||||
z-index: 1001;
|
||||
}
|
||||
|
||||
.sidebar.active {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.main-nav {
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-xs);
|
||||
}
|
||||
|
||||
.header-content {
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-sm);
|
||||
}
|
||||
|
||||
.cards-grid,
|
||||
.features-grid,
|
||||
.roles-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.doc-item {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.hero-title {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.hero-stats {
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-md);
|
||||
}
|
||||
}
|
||||
|
||||
/* Print Styles */
|
||||
@media print {
|
||||
.header,
|
||||
.sidebar,
|
||||
.viewer-toolbar,
|
||||
.toc,
|
||||
.footer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.viewer-main {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.markdown-body {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
337
Documentation/public/index.html
Normal file
337
Documentation/public/index.html
Normal file
@@ -0,0 +1,337 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="Goa-GEL Platform Documentation - Complete guides for users, testers, and developers">
|
||||
<title>Goa-GEL Documentation</title>
|
||||
<link rel="stylesheet" href="/css/styles.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<!-- Navigation Header -->
|
||||
<header class="header">
|
||||
<div class="container">
|
||||
<div class="header-content">
|
||||
<div class="logo">
|
||||
<h1>🏛️ Goa-GEL</h1>
|
||||
<p>Government e-Licensing Platform</p>
|
||||
</div>
|
||||
<nav class="main-nav">
|
||||
<a href="/" class="active">Home</a>
|
||||
<a href="/viewer.html?doc=USER_GUIDE">User Guide</a>
|
||||
<a href="/viewer.html?doc=E2E_TESTING_GUIDE">Testing</a>
|
||||
<a href="https://github.com/goa-gel" target="_blank">GitHub</a>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Hero Section -->
|
||||
<section class="hero">
|
||||
<div class="container">
|
||||
<h1 class="hero-title">📚 Platform Documentation</h1>
|
||||
<p class="hero-subtitle">Complete guides for all users, testers, and developers</p>
|
||||
<div class="hero-stats">
|
||||
<div class="stat">
|
||||
<span class="stat-number">5</span>
|
||||
<span class="stat-label">Comprehensive Guides</span>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<span class="stat-number">3,000+</span>
|
||||
<span class="stat-label">Lines of Documentation</span>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<span class="stat-number">8</span>
|
||||
<span class="stat-label">User Roles Covered</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Quick Start Section -->
|
||||
<section class="quick-start">
|
||||
<div class="container">
|
||||
<h2>🎯 Quick Start - Choose Your Path</h2>
|
||||
<div class="cards-grid">
|
||||
<a href="/viewer.html?doc=USER_GUIDE" class="card card-blue">
|
||||
<div class="card-icon">👤</div>
|
||||
<h3>I'm a User</h3>
|
||||
<p>Learn how to use the platform as an Admin, Department Officer, or Citizen</p>
|
||||
<span class="card-button">Open User Guide →</span>
|
||||
</a>
|
||||
|
||||
<a href="/viewer.html?doc=E2E_TESTING_GUIDE" class="card card-green">
|
||||
<div class="card-icon">🧪</div>
|
||||
<h3>I Need to Test</h3>
|
||||
<p>Complete end-to-end testing guide with 20 detailed scenarios</p>
|
||||
<span class="card-button">Open Testing Guide →</span>
|
||||
</a>
|
||||
|
||||
<a href="/viewer.html?doc=IMPLEMENTATION_COMPLETE" class="card card-purple">
|
||||
<div class="card-icon">💻</div>
|
||||
<h3>I'm a Developer</h3>
|
||||
<p>Understand the implementation, architecture, and codebase</p>
|
||||
<span class="card-button">Open Dev Guide →</span>
|
||||
</a>
|
||||
|
||||
<a href="/viewer.html?doc=ARCHITECTURE_GUIDE" class="card card-orange">
|
||||
<div class="card-icon">🏗️</div>
|
||||
<h3>I'm an Architect</h3>
|
||||
<p>Deep dive into technical architecture and system design</p>
|
||||
<span class="card-button">Open Architecture →</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Documentation Library -->
|
||||
<section class="documentation">
|
||||
<div class="container">
|
||||
<h2>📖 Complete Documentation Library</h2>
|
||||
|
||||
<!-- User Documentation -->
|
||||
<div class="doc-category">
|
||||
<h3>👥 User Documentation</h3>
|
||||
<div class="doc-list">
|
||||
<div class="doc-item">
|
||||
<div class="doc-info">
|
||||
<h4>📘 User Guide</h4>
|
||||
<p>Complete manual for all user roles - Administrators, Department Officers, and Citizens</p>
|
||||
<div class="doc-meta">
|
||||
<span class="badge">650+ lines</span>
|
||||
<span class="badge">All Roles</span>
|
||||
<span class="badge">Step-by-Step</span>
|
||||
</div>
|
||||
</div>
|
||||
<a href="/viewer.html?doc=USER_GUIDE" class="btn btn-primary">Read Guide</a>
|
||||
</div>
|
||||
|
||||
<div class="doc-item">
|
||||
<h4>📚 Documentation Index</h4>
|
||||
<p>Master navigation guide with role-based learning paths</p>
|
||||
<div class="doc-meta">
|
||||
<span class="badge">Quick Navigation</span>
|
||||
<span class="badge">11 Guides</span>
|
||||
</div>
|
||||
<a href="/viewer.html?doc=DOCUMENTATION_INDEX" class="btn btn-secondary">View Index</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Testing Documentation -->
|
||||
<div class="doc-category">
|
||||
<h3>🧪 Testing & QA</h3>
|
||||
<div class="doc-list">
|
||||
<div class="doc-item">
|
||||
<div class="doc-info">
|
||||
<h4>🧪 E2E Testing Guide</h4>
|
||||
<p>Comprehensive testing scenarios covering complete license approval workflow</p>
|
||||
<div class="doc-meta">
|
||||
<span class="badge">600+ lines</span>
|
||||
<span class="badge">20 Scenarios</span>
|
||||
<span class="badge">QA Ready</span>
|
||||
</div>
|
||||
</div>
|
||||
<a href="/viewer.html?doc=E2E_TESTING_GUIDE" class="btn btn-primary">Open Testing Guide</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Developer Documentation -->
|
||||
<div class="doc-category">
|
||||
<h3>💻 Developer Documentation</h3>
|
||||
<div class="doc-list">
|
||||
<div class="doc-item">
|
||||
<div class="doc-info">
|
||||
<h4>📊 Implementation Complete</h4>
|
||||
<p>Complete implementation status, components created, and API endpoints</p>
|
||||
<div class="doc-meta">
|
||||
<span class="badge">100% Complete</span>
|
||||
<span class="badge">45+ Components</span>
|
||||
<span class="badge">13 Endpoints</span>
|
||||
</div>
|
||||
</div>
|
||||
<a href="/viewer.html?doc=IMPLEMENTATION_COMPLETE" class="btn btn-primary">View Status</a>
|
||||
</div>
|
||||
|
||||
<div class="doc-item">
|
||||
<div class="doc-info">
|
||||
<h4>🏗️ Architecture Guide</h4>
|
||||
<p>Technical architecture, blockchain integration, and deployment</p>
|
||||
<div class="doc-meta">
|
||||
<span class="badge">1000+ lines</span>
|
||||
<span class="badge">C4 Model</span>
|
||||
<span class="badge">Deep Dive</span>
|
||||
</div>
|
||||
</div>
|
||||
<a href="/viewer.html?doc=ARCHITECTURE_GUIDE" class="btn btn-primary">Read Architecture</a>
|
||||
</div>
|
||||
|
||||
<div class="doc-item">
|
||||
<div class="doc-info">
|
||||
<h4>⚡ Quick Start</h4>
|
||||
<p>Set up and run the platform locally in minutes</p>
|
||||
<div class="doc-meta">
|
||||
<span class="badge">Setup Guide</span>
|
||||
<span class="badge">10 mins</span>
|
||||
</div>
|
||||
</div>
|
||||
<a href="/viewer.html?doc=QUICK_START" class="btn btn-secondary">Quick Setup</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Features Section -->
|
||||
<section class="features">
|
||||
<div class="container">
|
||||
<h2>✨ Documentation Features</h2>
|
||||
<div class="features-grid">
|
||||
<div class="feature">
|
||||
<div class="feature-icon">📱</div>
|
||||
<h3>Mobile Friendly</h3>
|
||||
<p>Responsive design works on all devices</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<div class="feature-icon">🔍</div>
|
||||
<h3>Searchable</h3>
|
||||
<p>Quick search across all documentation</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<div class="feature-icon">🎨</div>
|
||||
<h3>Syntax Highlighting</h3>
|
||||
<p>Beautiful code examples with highlighting</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<div class="feature-icon">📥</div>
|
||||
<h3>Downloadable</h3>
|
||||
<p>Download guides as PDF or Markdown</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<div class="feature-icon">🌐</div>
|
||||
<h3>Multi-Language</h3>
|
||||
<p>English with Hindi translations coming soon</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<div class="feature-icon">🔄</div>
|
||||
<h3>Always Updated</h3>
|
||||
<p>Documentation synced with platform updates</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Role-Based Guides -->
|
||||
<section class="roles">
|
||||
<div class="container">
|
||||
<h2>👤 Documentation by Role</h2>
|
||||
<div class="roles-grid">
|
||||
<div class="role-card">
|
||||
<h3>👨💼 Administrator</h3>
|
||||
<ul>
|
||||
<li>Admin portal guide</li>
|
||||
<li>Department onboarding</li>
|
||||
<li>User management</li>
|
||||
<li>Monitoring dashboards</li>
|
||||
</ul>
|
||||
<a href="/viewer.html?doc=USER_GUIDE#administrators" class="btn btn-sm">View Guide</a>
|
||||
</div>
|
||||
|
||||
<div class="role-card">
|
||||
<h3>🏛️ Department Officer</h3>
|
||||
<ul>
|
||||
<li>Application review</li>
|
||||
<li>Approval workflow</li>
|
||||
<li>Document verification</li>
|
||||
<li>Change requests</li>
|
||||
</ul>
|
||||
<a href="/viewer.html?doc=USER_GUIDE#department-officers" class="btn btn-sm">View Guide</a>
|
||||
</div>
|
||||
|
||||
<div class="role-card">
|
||||
<h3>👥 Citizen</h3>
|
||||
<ul>
|
||||
<li>Account creation</li>
|
||||
<li>License application</li>
|
||||
<li>Document upload</li>
|
||||
<li>Status tracking</li>
|
||||
</ul>
|
||||
<a href="/viewer.html?doc=USER_GUIDE#citizens" class="btn btn-sm">View Guide</a>
|
||||
</div>
|
||||
|
||||
<div class="role-card">
|
||||
<h3>💻 Developer</h3>
|
||||
<ul>
|
||||
<li>Setup & installation</li>
|
||||
<li>Code architecture</li>
|
||||
<li>API documentation</li>
|
||||
<li>Deployment guide</li>
|
||||
</ul>
|
||||
<a href="/viewer.html?doc=IMPLEMENTATION_COMPLETE" class="btn btn-sm">View Guide</a>
|
||||
</div>
|
||||
|
||||
<div class="role-card">
|
||||
<h3>🧪 QA Engineer</h3>
|
||||
<ul>
|
||||
<li>Test scenarios</li>
|
||||
<li>E2E workflows</li>
|
||||
<li>Error testing</li>
|
||||
<li>Performance tests</li>
|
||||
</ul>
|
||||
<a href="/viewer.html?doc=E2E_TESTING_GUIDE" class="btn btn-sm">View Guide</a>
|
||||
</div>
|
||||
|
||||
<div class="role-card">
|
||||
<h3>🔧 DevOps</h3>
|
||||
<ul>
|
||||
<li>Docker deployment</li>
|
||||
<li>Infrastructure setup</li>
|
||||
<li>Monitoring config</li>
|
||||
<li>Security hardening</li>
|
||||
</ul>
|
||||
<a href="/viewer.html?doc=ARCHITECTURE_GUIDE#deployment" class="btn btn-sm">View Guide</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="footer">
|
||||
<div class="container">
|
||||
<div class="footer-content">
|
||||
<div class="footer-section">
|
||||
<h4>Goa-GEL Platform</h4>
|
||||
<p>Government e-Licensing</p>
|
||||
<p>Blockchain-based document verification</p>
|
||||
</div>
|
||||
<div class="footer-section">
|
||||
<h4>Documentation</h4>
|
||||
<a href="/viewer.html?doc=USER_GUIDE">User Guide</a>
|
||||
<a href="/viewer.html?doc=E2E_TESTING_GUIDE">Testing Guide</a>
|
||||
<a href="/viewer.html?doc=IMPLEMENTATION_COMPLETE">Dev Guide</a>
|
||||
</div>
|
||||
<div class="footer-section">
|
||||
<h4>Resources</h4>
|
||||
<a href="/viewer.html?doc=ARCHITECTURE_GUIDE">Architecture</a>
|
||||
<a href="/viewer.html?doc=QUICK_START">Quick Start</a>
|
||||
<a href="/viewer.html?doc=DOCUMENTATION_INDEX">All Docs</a>
|
||||
</div>
|
||||
<div class="footer-section">
|
||||
<h4>Support</h4>
|
||||
<p>Email: support@goa.gov.in</p>
|
||||
<p>Phone: +91-832-XXXXXXX</p>
|
||||
<p>Hours: Mon-Fri, 9 AM - 6 PM IST</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer-bottom">
|
||||
<p>© 2026 Government of Goa. All rights reserved.</p>
|
||||
<p>Version 1.0 | Last Updated: February 2026</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="/js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
27
Documentation/public/js/main.js
Normal file
27
Documentation/public/js/main.js
Normal file
@@ -0,0 +1,27 @@
|
||||
// Main.js - Homepage functionality
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
console.log('Goa-GEL Documentation loaded');
|
||||
|
||||
// Smooth scrolling for anchor links
|
||||
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
||||
anchor.addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
const target = document.querySelector(this.getAttribute('href'));
|
||||
if (target) {
|
||||
target.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'start'
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Highlight active navigation link
|
||||
const currentPath = window.location.pathname;
|
||||
document.querySelectorAll('.main-nav a').forEach(link => {
|
||||
if (link.getAttribute('href') === currentPath) {
|
||||
link.classList.add('active');
|
||||
}
|
||||
});
|
||||
});
|
||||
322
Documentation/public/js/viewer.js
Normal file
322
Documentation/public/js/viewer.js
Normal file
@@ -0,0 +1,322 @@
|
||||
// Viewer.js - Document viewer functionality
|
||||
|
||||
// Configuration
|
||||
const DOC_MAP = {
|
||||
'USER_GUIDE': '/docs/USER_GUIDE.md',
|
||||
'E2E_TESTING_GUIDE': '/docs/E2E_TESTING_GUIDE.md',
|
||||
'IMPLEMENTATION_COMPLETE': '/docs/IMPLEMENTATION_COMPLETE.md',
|
||||
'ARCHITECTURE_GUIDE': '/docs/ARCHITECTURE_GUIDE.md',
|
||||
'QUICK_START': '/docs/QUICK_START.md',
|
||||
'DOCUMENTATION_INDEX': '/docs/DOCUMENTATION_INDEX.md',
|
||||
'IMPLEMENTATION_SUMMARY': '/docs/IMPLEMENTATION_SUMMARY.md'
|
||||
};
|
||||
|
||||
let currentDoc = null;
|
||||
let sidebarOpen = true;
|
||||
|
||||
// Initialize on page load
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
initViewer();
|
||||
setupEventListeners();
|
||||
loadDocumentFromURL();
|
||||
});
|
||||
|
||||
function initViewer() {
|
||||
// Configure marked.js
|
||||
if (typeof marked !== 'undefined') {
|
||||
marked.setOptions({
|
||||
breaks: true,
|
||||
gfm: true,
|
||||
highlight: function(code, lang) {
|
||||
if (typeof hljs !== 'undefined' && lang && hljs.getLanguage(lang)) {
|
||||
try {
|
||||
return hljs.highlight(code, { language: lang }).value;
|
||||
} catch (err) {
|
||||
console.error('Highlight error:', err);
|
||||
}
|
||||
}
|
||||
return code;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function setupEventListeners() {
|
||||
// Sidebar toggle
|
||||
const toggleBtn = document.getElementById('toggle-sidebar');
|
||||
if (toggleBtn) {
|
||||
toggleBtn.addEventListener('click', toggleSidebar);
|
||||
}
|
||||
|
||||
// Document selector
|
||||
const docSelector = document.getElementById('doc-selector');
|
||||
if (docSelector) {
|
||||
docSelector.addEventListener('change', function() {
|
||||
if (this.value) {
|
||||
loadDocument(this.value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Print button
|
||||
const printBtn = document.getElementById('print-doc');
|
||||
if (printBtn) {
|
||||
printBtn.addEventListener('click', function() {
|
||||
window.print();
|
||||
});
|
||||
}
|
||||
|
||||
// Download button
|
||||
const downloadBtn = document.getElementById('download-doc');
|
||||
if (downloadBtn) {
|
||||
downloadBtn.addEventListener('click', downloadCurrentDoc);
|
||||
}
|
||||
|
||||
// Sidebar navigation links
|
||||
document.querySelectorAll('.sidebar-nav .nav-link').forEach(link => {
|
||||
link.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
const url = this.getAttribute('href');
|
||||
const params = new URLSearchParams(url.split('?')[1]);
|
||||
const doc = params.get('doc');
|
||||
if (doc) {
|
||||
loadDocument(doc);
|
||||
updateURL(doc);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function loadDocumentFromURL() {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const doc = params.get('doc');
|
||||
if (doc && DOC_MAP[doc]) {
|
||||
loadDocument(doc);
|
||||
} else {
|
||||
showError('No document specified. Please select a document from the navigation.');
|
||||
}
|
||||
}
|
||||
|
||||
async function loadDocument(docKey) {
|
||||
const docPath = DOC_MAP[docKey];
|
||||
if (!docPath) {
|
||||
showError(`Document "${docKey}" not found`);
|
||||
return;
|
||||
}
|
||||
|
||||
currentDoc = docKey;
|
||||
showLoading();
|
||||
hideError();
|
||||
|
||||
try {
|
||||
const response = await fetch(docPath);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const markdown = await response.text();
|
||||
renderMarkdown(markdown);
|
||||
generateTOC();
|
||||
updateActiveNav(docKey);
|
||||
updateDocSelector(docKey);
|
||||
updateURL(docKey);
|
||||
|
||||
// Scroll to top
|
||||
window.scrollTo(0, 0);
|
||||
} catch (error) {
|
||||
console.error('Error loading document:', error);
|
||||
showError(`Failed to load document: ${error.message}`);
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
function renderMarkdown(markdown) {
|
||||
const contentDiv = document.getElementById('content');
|
||||
if (!contentDiv) return;
|
||||
|
||||
try {
|
||||
// Parse markdown to HTML
|
||||
const html = marked.parse(markdown);
|
||||
|
||||
// Sanitize HTML using DOMPurify if available, otherwise use trusted content
|
||||
const safeHTML = (typeof DOMPurify !== 'undefined')
|
||||
? DOMPurify.sanitize(html, { ADD_ATTR: ['target'] })
|
||||
: html;
|
||||
|
||||
contentDiv.innerHTML = safeHTML;
|
||||
|
||||
// Syntax highlighting for code blocks
|
||||
if (typeof hljs !== 'undefined') {
|
||||
contentDiv.querySelectorAll('pre code').forEach((block) => {
|
||||
hljs.highlightElement(block);
|
||||
});
|
||||
}
|
||||
|
||||
// Make external links open in new tab
|
||||
contentDiv.querySelectorAll('a[href^="http"]').forEach(link => {
|
||||
link.setAttribute('target', '_blank');
|
||||
link.setAttribute('rel', 'noopener noreferrer');
|
||||
});
|
||||
|
||||
// Add IDs to headings for anchor links
|
||||
contentDiv.querySelectorAll('h1, h2, h3, h4, h5, h6').forEach((heading, index) => {
|
||||
if (!heading.id) {
|
||||
heading.id = `heading-${index}`;
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error rendering markdown:', error);
|
||||
showError(`Failed to render document: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
function generateTOC() {
|
||||
const tocContent = document.getElementById('toc-content');
|
||||
const content = document.getElementById('content');
|
||||
|
||||
if (!tocContent || !content) return;
|
||||
|
||||
const headings = content.querySelectorAll('h2, h3');
|
||||
if (headings.length === 0) {
|
||||
tocContent.textContent = 'No headings found';
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear existing content
|
||||
tocContent.innerHTML = '';
|
||||
|
||||
// Create TOC links using DOM methods
|
||||
headings.forEach(heading => {
|
||||
const level = heading.tagName.toLowerCase();
|
||||
const text = heading.textContent;
|
||||
const id = heading.id || `heading-${text.replace(/\s+/g, '-').toLowerCase()}`;
|
||||
heading.id = id;
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = `#${id}`;
|
||||
link.textContent = text;
|
||||
|
||||
if (level === 'h3') {
|
||||
link.style.marginLeft = '1rem';
|
||||
}
|
||||
|
||||
link.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
const target = document.getElementById(id);
|
||||
if (target) {
|
||||
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}
|
||||
});
|
||||
|
||||
tocContent.appendChild(link);
|
||||
});
|
||||
}
|
||||
|
||||
function toggleSidebar() {
|
||||
const sidebar = document.querySelector('.sidebar');
|
||||
const icon = document.getElementById('sidebar-icon');
|
||||
|
||||
if (sidebar) {
|
||||
sidebarOpen = !sidebarOpen;
|
||||
sidebar.classList.toggle('active');
|
||||
if (icon) {
|
||||
icon.textContent = sidebarOpen ? '☰' : '✕';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateActiveNav(docKey) {
|
||||
document.querySelectorAll('.sidebar-nav .nav-link').forEach(link => {
|
||||
link.classList.remove('active');
|
||||
const url = link.getAttribute('href');
|
||||
if (url && url.includes(`doc=${docKey}`)) {
|
||||
link.classList.add('active');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateDocSelector(docKey) {
|
||||
const selector = document.getElementById('doc-selector');
|
||||
if (selector) {
|
||||
selector.value = docKey;
|
||||
}
|
||||
}
|
||||
|
||||
function updateURL(docKey) {
|
||||
const newURL = `${window.location.pathname}?doc=${docKey}`;
|
||||
window.history.pushState({ doc: docKey }, '', newURL);
|
||||
}
|
||||
|
||||
function downloadCurrentDoc() {
|
||||
if (!currentDoc) {
|
||||
alert('No document loaded');
|
||||
return;
|
||||
}
|
||||
|
||||
const docPath = DOC_MAP[currentDoc];
|
||||
const filename = docPath.split('/').pop();
|
||||
|
||||
fetch(docPath)
|
||||
.then(response => response.text())
|
||||
.then(text => {
|
||||
const blob = new Blob([text], { type: 'text/markdown' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = filename;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Download error:', error);
|
||||
alert('Failed to download document');
|
||||
});
|
||||
}
|
||||
|
||||
function showLoading() {
|
||||
const loading = document.getElementById('loading');
|
||||
const content = document.getElementById('content');
|
||||
|
||||
if (loading) loading.style.display = 'block';
|
||||
if (content) content.style.display = 'none';
|
||||
}
|
||||
|
||||
function hideLoading() {
|
||||
const loading = document.getElementById('loading');
|
||||
const content = document.getElementById('content');
|
||||
|
||||
if (loading) loading.style.display = 'none';
|
||||
if (content) content.style.display = 'block';
|
||||
}
|
||||
|
||||
function showError(message) {
|
||||
const error = document.getElementById('error');
|
||||
const errorMessage = document.getElementById('error-message');
|
||||
const content = document.getElementById('content');
|
||||
|
||||
if (error) error.style.display = 'block';
|
||||
if (errorMessage) errorMessage.textContent = message;
|
||||
if (content) content.style.display = 'none';
|
||||
}
|
||||
|
||||
function hideError() {
|
||||
const error = document.getElementById('error');
|
||||
if (error) error.style.display = 'none';
|
||||
}
|
||||
|
||||
// Handle browser back/forward buttons
|
||||
window.addEventListener('popstate', function(event) {
|
||||
if (event.state && event.state.doc) {
|
||||
loadDocument(event.state.doc);
|
||||
}
|
||||
});
|
||||
|
||||
// Search functionality (future enhancement)
|
||||
function searchDocumentation(query) {
|
||||
// TODO: Implement search across all documentation
|
||||
console.log('Search query:', query);
|
||||
}
|
||||
146
Documentation/public/viewer.html
Normal file
146
Documentation/public/viewer.html
Normal file
@@ -0,0 +1,146 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Documentation Viewer - Goa-GEL</title>
|
||||
<link rel="stylesheet" href="/css/styles.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.0.6/purify.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Navigation Header -->
|
||||
<header class="header">
|
||||
<div class="container">
|
||||
<div class="header-content">
|
||||
<div class="logo">
|
||||
<a href="/">
|
||||
<h1>🏛️ Goa-GEL</h1>
|
||||
<p>Documentation</p>
|
||||
</a>
|
||||
</div>
|
||||
<nav class="main-nav">
|
||||
<a href="/">Home</a>
|
||||
<a href="/viewer.html?doc=USER_GUIDE">User Guide</a>
|
||||
<a href="/viewer.html?doc=E2E_TESTING_GUIDE">Testing</a>
|
||||
<a href="/viewer.html?doc=DOCUMENTATION_INDEX">All Docs</a>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Document Viewer -->
|
||||
<div class="viewer-container">
|
||||
<!-- Sidebar -->
|
||||
<aside class="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<h3>📚 Documentation</h3>
|
||||
</div>
|
||||
<nav class="sidebar-nav">
|
||||
<div class="nav-section">
|
||||
<h4>User Guides</h4>
|
||||
<a href="/viewer.html?doc=USER_GUIDE" class="nav-link">
|
||||
<span class="nav-icon">📘</span>
|
||||
User Guide
|
||||
</a>
|
||||
<a href="/viewer.html?doc=DOCUMENTATION_INDEX" class="nav-link">
|
||||
<span class="nav-icon">📚</span>
|
||||
Documentation Index
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="nav-section">
|
||||
<h4>Testing & QA</h4>
|
||||
<a href="/viewer.html?doc=E2E_TESTING_GUIDE" class="nav-link">
|
||||
<span class="nav-icon">🧪</span>
|
||||
E2E Testing Guide
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="nav-section">
|
||||
<h4>Development</h4>
|
||||
<a href="/viewer.html?doc=IMPLEMENTATION_COMPLETE" class="nav-link">
|
||||
<span class="nav-icon">📊</span>
|
||||
Implementation Status
|
||||
</a>
|
||||
<a href="/viewer.html?doc=ARCHITECTURE_GUIDE" class="nav-link">
|
||||
<span class="nav-icon">🏗️</span>
|
||||
Architecture Guide
|
||||
</a>
|
||||
<a href="/viewer.html?doc=QUICK_START" class="nav-link">
|
||||
<span class="nav-icon">⚡</span>
|
||||
Quick Start
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="nav-section">
|
||||
<h4>Additional</h4>
|
||||
<a href="/viewer.html?doc=IMPLEMENTATION_SUMMARY" class="nav-link">
|
||||
<span class="nav-icon">📝</span>
|
||||
Implementation Summary
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="viewer-main">
|
||||
<!-- Toolbar -->
|
||||
<div class="viewer-toolbar">
|
||||
<div class="toolbar-left">
|
||||
<button class="btn btn-sm" id="toggle-sidebar">
|
||||
<span id="sidebar-icon">☰</span> Menu
|
||||
</button>
|
||||
<select id="doc-selector" class="doc-selector">
|
||||
<option value="">Select Document...</option>
|
||||
<optgroup label="User Guides">
|
||||
<option value="USER_GUIDE">📘 User Guide</option>
|
||||
<option value="DOCUMENTATION_INDEX">📚 Documentation Index</option>
|
||||
</optgroup>
|
||||
<optgroup label="Testing & QA">
|
||||
<option value="E2E_TESTING_GUIDE">🧪 E2E Testing Guide</option>
|
||||
</optgroup>
|
||||
<optgroup label="Development">
|
||||
<option value="IMPLEMENTATION_COMPLETE">📊 Implementation Status</option>
|
||||
<option value="ARCHITECTURE_GUIDE">🏗️ Architecture Guide</option>
|
||||
<option value="QUICK_START">⚡ Quick Start</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
</div>
|
||||
<div class="toolbar-right">
|
||||
<button class="btn btn-sm" id="print-doc" title="Print">
|
||||
🖨️ Print
|
||||
</button>
|
||||
<button class="btn btn-sm" id="download-doc" title="Download">
|
||||
📥 Download
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Document Content -->
|
||||
<div class="document-content">
|
||||
<div id="loading" class="loading">
|
||||
<div class="spinner"></div>
|
||||
<p>Loading documentation...</p>
|
||||
</div>
|
||||
<div id="error" class="error" style="display: none;">
|
||||
<h2>⚠️ Error Loading Document</h2>
|
||||
<p id="error-message"></p>
|
||||
<button class="btn" onclick="location.reload()">Retry</button>
|
||||
</div>
|
||||
<article id="content" class="markdown-body"></article>
|
||||
</div>
|
||||
|
||||
<!-- Table of Contents (floating) -->
|
||||
<nav class="toc" id="toc">
|
||||
<h4>📑 On This Page</h4>
|
||||
<div id="toc-content"></div>
|
||||
</nav>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="/js/viewer.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user