From 4bc831007090fe7d11cf1a240808606a968f97f2 Mon Sep 17 00:00:00 2001 From: Mondo Diaz Date: Wed, 15 Oct 2025 08:30:21 -0500 Subject: [PATCH] Rebrand to Obsidian with modern UI and auto-refresh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Renamed project from "Test Artifact Data Lake" to "Obsidian" - Updated all branding across README, quickstart scripts, and API - Implemented dark mode theme with professional color palette - Simplified table to 4 essential columns (Sim Source, Artifacts, Date, Uploaded By) - Replaced emoji icons with Lucide SVG icons for better scaling - Added auto-refresh functionality (5-second intervals, toggleable) - Enhanced UI with modern flexbox layouts and hover effects - Updated upload form labels to match new terminology 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- README.md | 4 +- app/main.py | 8 +-- quickstart.bat | 2 +- quickstart.ps1 | 2 +- quickstart.sh | 2 +- static/css/styles.css | 158 ++++++++++++++++++++++++++---------------- static/index.html | 67 +++++++++++------- static/js/app.js | 74 ++++++++++++++++---- 8 files changed, 209 insertions(+), 108 deletions(-) diff --git a/README.md b/README.md index 31127db..70a217c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -# Test Artifact Data Lake +# Obsidian + +**Enterprise Test Artifact Storage** A lightweight, cloud-native API for storing and querying test artifacts including CSV files, JSON files, binary files, and packet captures (PCAP). Built with FastAPI and supports both AWS S3 and self-hosted MinIO storage backends. diff --git a/app/main.py b/app/main.py index c5d3770..b1e0034 100644 --- a/app/main.py +++ b/app/main.py @@ -19,8 +19,8 @@ logger = logging.getLogger(__name__) # Create FastAPI app app = FastAPI( - title="Test Artifact Data Lake", - description="API for storing and querying test artifacts including CSV, JSON, binary files, and packet captures", + title="Obsidian", + description="Enterprise Test Artifact Storage - API for storing and querying test artifacts including CSV, JSON, binary files, and packet captures", version="1.0.0", docs_url="/docs", redoc_url="/redoc" @@ -59,7 +59,7 @@ async def startup_event(): async def api_root(): """API root endpoint""" return { - "message": "Test Artifact Data Lake API", + "message": "Obsidian - Enterprise Test Artifact Storage", "version": "1.0.0", "docs": "/docs", "deployment_mode": settings.deployment_mode, @@ -75,7 +75,7 @@ async def ui_root(): return FileResponse(index_path) else: return { - "message": "Test Artifact Data Lake API", + "message": "Obsidian - Enterprise Test Artifact Storage", "version": "1.0.0", "docs": "/docs", "ui": "UI not found. Serving API only.", diff --git a/quickstart.bat b/quickstart.bat index be8fc12..ee4953b 100644 --- a/quickstart.bat +++ b/quickstart.bat @@ -2,7 +2,7 @@ setlocal enabledelayedexpansion echo ========================================= -echo Test Artifact Data Lake - Quick Start +echo Obsidian - Quick Start echo ========================================= echo. diff --git a/quickstart.ps1 b/quickstart.ps1 index 09373cb..797cf18 100644 --- a/quickstart.ps1 +++ b/quickstart.ps1 @@ -1,7 +1,7 @@ # Test Artifact Data Lake - Quick Start (PowerShell) Write-Host "=========================================" -ForegroundColor Cyan -Write-Host "Test Artifact Data Lake - Quick Start" -ForegroundColor Cyan +Write-Host "Obsidian - Quick Start" -ForegroundColor Cyan Write-Host "=========================================" -ForegroundColor Cyan Write-Host "" diff --git a/quickstart.sh b/quickstart.sh index e963763..a7e93ee 100755 --- a/quickstart.sh +++ b/quickstart.sh @@ -3,7 +3,7 @@ set -e echo "=========================================" -echo "Test Artifact Data Lake - Quick Start" +echo "Obsidian - Quick Start" echo "=========================================" echo "" diff --git a/static/css/styles.css b/static/css/styles.css index 2a73e1e..145f04a 100644 --- a/static/css/styles.css +++ b/static/css/styles.css @@ -6,22 +6,23 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + background: #0f172a; min-height: 100vh; padding: 20px; + color: #e2e8f0; } .container { max-width: 1400px; margin: 0 auto; - background: white; + background: #1e293b; border-radius: 12px; - box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5); overflow: hidden; } header { - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + background: linear-gradient(135deg, #1e3a8a 0%, #4338ca 100%); color: white; padding: 30px; display: flex; @@ -51,8 +52,8 @@ header h1 { .tabs { display: flex; - background: #f7f9fc; - border-bottom: 2px solid #e2e8f0; + background: #0f172a; + border-bottom: 2px solid #334155; } .tab-button { @@ -64,17 +65,22 @@ header h1 { font-weight: 500; cursor: pointer; transition: all 0.3s; - color: #64748b; + color: #94a3b8; + display: inline-flex; + align-items: center; + justify-content: center; + gap: 8px; } .tab-button:hover { - background: #e2e8f0; + background: #1e293b; + color: #e2e8f0; } .tab-button.active { - background: white; - color: #667eea; - border-bottom: 3px solid #667eea; + background: #1e293b; + color: #60a5fa; + border-bottom: 3px solid #60a5fa; } .tab-content { @@ -101,25 +107,28 @@ header h1 { font-weight: 500; cursor: pointer; transition: all 0.3s; + display: inline-flex; + align-items: center; + gap: 8px; } .btn-primary { - background: #667eea; + background: #3b82f6; color: white; } .btn-primary:hover { - background: #5568d3; + background: #2563eb; transform: translateY(-1px); } .btn-secondary { - background: #e2e8f0; - color: #475569; + background: #334155; + color: #e2e8f0; } .btn-secondary:hover { - background: #cbd5e1; + background: #475569; } .btn-danger { @@ -142,8 +151,8 @@ header h1 { } .count-badge { - background: #f0f9ff; - color: #0369a1; + background: #1e3a8a; + color: #93c5fd; padding: 8px 16px; border-radius: 20px; font-size: 13px; @@ -153,8 +162,9 @@ header h1 { .table-container { overflow-x: auto; - border: 1px solid #e2e8f0; + border: 1px solid #334155; border-radius: 8px; + background: #0f172a; } table { @@ -164,30 +174,34 @@ table { } thead { - background: #f7f9fc; + background: #1e293b; } th { padding: 14px 12px; text-align: left; font-weight: 600; - color: #475569; - border-bottom: 2px solid #e2e8f0; + color: #94a3b8; + border-bottom: 2px solid #334155; white-space: nowrap; + text-transform: uppercase; + font-size: 12px; + letter-spacing: 0.5px; } td { - padding: 12px; - border-bottom: 1px solid #e2e8f0; + padding: 16px 12px; + border-bottom: 1px solid #1e293b; + color: #cbd5e1; } tbody tr:hover { - background: #f7f9fc; + background: #1e293b; } .loading { text-align: center; - color: #94a3b8; + color: #64748b; padding: 40px !important; } @@ -200,29 +214,29 @@ tbody tr:hover { } .result-pass { - background: #d1fae5; - color: #065f46; + background: #064e3b; + color: #6ee7b7; } .result-fail { - background: #fee2e2; - color: #991b1b; + background: #7f1d1d; + color: #fca5a5; } .result-skip { - background: #fef3c7; - color: #92400e; + background: #78350f; + color: #fcd34d; } .result-error { - background: #fecaca; - color: #7f1d1d; + background: #7f1d1d; + color: #fca5a5; } .tag { display: inline-block; - background: #e0e7ff; - color: #3730a3; + background: #1e3a8a; + color: #93c5fd; padding: 3px 8px; border-radius: 10px; font-size: 11px; @@ -230,8 +244,8 @@ tbody tr:hover { } .file-type-badge { - background: #dbeafe; - color: #1e40af; + background: #1e3a8a; + color: #93c5fd; padding: 4px 8px; border-radius: 6px; font-size: 11px; @@ -250,7 +264,7 @@ tbody tr:hover { #page-info { font-weight: 500; - color: #64748b; + color: #94a3b8; } .upload-section, .query-section { @@ -271,7 +285,7 @@ tbody tr:hover { label { display: block; font-weight: 500; - color: #475569; + color: #cbd5e1; margin-bottom: 6px; font-size: 14px; } @@ -283,23 +297,25 @@ select, textarea { width: 100%; padding: 10px 14px; - border: 1px solid #e2e8f0; + border: 1px solid #334155; border-radius: 6px; font-size: 14px; font-family: inherit; transition: border-color 0.3s; + background: #0f172a; + color: #e2e8f0; } input:focus, select:focus, textarea:focus { outline: none; - border-color: #667eea; - box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); + border-color: #3b82f6; + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); } small { - color: #94a3b8; + color: #64748b; font-size: 12px; display: block; margin-top: 4px; @@ -313,14 +329,14 @@ small { } #upload-status.success { - background: #d1fae5; - color: #065f46; + background: #064e3b; + color: #6ee7b7; display: block; } #upload-status.error { - background: #fee2e2; - color: #991b1b; + background: #7f1d1d; + color: #fca5a5; display: block; } @@ -332,7 +348,7 @@ small { top: 0; width: 100%; height: 100%; - background: rgba(0, 0, 0, 0.5); + background: rgba(0, 0, 0, 0.8); backdrop-filter: blur(4px); } @@ -343,14 +359,15 @@ small { } .modal-content { - background: white; + background: #1e293b; padding: 30px; border-radius: 12px; max-width: 700px; max-height: 80vh; overflow-y: auto; position: relative; - box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5); + border: 1px solid #334155; } .close { @@ -359,19 +376,19 @@ small { top: 20px; font-size: 28px; font-weight: bold; - color: #94a3b8; + color: #64748b; cursor: pointer; transition: color 0.3s; } .close:hover { - color: #475569; + color: #e2e8f0; } .detail-row { margin-bottom: 16px; padding-bottom: 16px; - border-bottom: 1px solid #e2e8f0; + border-bottom: 1px solid #334155; } .detail-row:last-child { @@ -380,20 +397,29 @@ small { .detail-label { font-weight: 600; - color: #475569; + color: #94a3b8; margin-bottom: 4px; } .detail-value { - color: #64748b; + color: #cbd5e1; } pre { - background: #f7f9fc; + background: #0f172a; padding: 12px; border-radius: 6px; overflow-x: auto; font-size: 12px; + border: 1px solid #334155; +} + +code { + background: #0f172a; + padding: 2px 6px; + border-radius: 4px; + font-size: 12px; + color: #93c5fd; } .action-buttons { @@ -405,14 +431,24 @@ pre { background: none; border: none; cursor: pointer; - font-size: 18px; - padding: 6px; + padding: 8px; border-radius: 4px; - transition: background 0.3s; + transition: all 0.3s; + color: #94a3b8; + display: inline-flex; + align-items: center; + justify-content: center; } .icon-btn:hover { - background: #e2e8f0; + background: #334155; + color: #e2e8f0; + transform: scale(1.1); +} + +/* Ensure SVG icons inherit color */ +.icon-btn svg { + stroke: currentColor; } @media (max-width: 768px) { diff --git a/static/index.html b/static/index.html index 4fcd19c..760855b 100644 --- a/static/index.html +++ b/static/index.html @@ -3,13 +3,14 @@ - Test Artifact Data Lake + Obsidian - Test Artifact Data Lake +
-

🗄️ Test Artifact Data Lake

+

◆ Obsidian

@@ -17,16 +18,29 @@
- - + + +
@@ -34,21 +48,16 @@ - - - - - - - - - + + + + - +
IDFilenameTypeSizeTest NameSuiteResultTagsCreatedSim SourceArtifactsDateUploaded By Actions
Loading artifacts...Loading artifacts...
@@ -74,12 +83,12 @@
- - + +
- - + +
@@ -120,7 +129,9 @@
- +
@@ -187,8 +198,12 @@ - - + + @@ -204,5 +219,9 @@ + diff --git a/static/js/app.js b/static/js/app.js index c7ff00a..f752731 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -6,10 +6,16 @@ let currentPage = 1; let pageSize = 25; let totalArtifacts = 0; +// Auto-refresh +let autoRefreshEnabled = true; +let autoRefreshInterval = null; +const REFRESH_INTERVAL_MS = 5000; // 5 seconds + // Load API info on page load window.addEventListener('DOMContentLoaded', () => { loadApiInfo(); loadArtifacts(); + startAutoRefresh(); }); // Load API information @@ -48,33 +54,28 @@ function displayArtifacts(artifacts) { const tbody = document.getElementById('artifacts-tbody'); if (artifacts.length === 0) { - tbody.innerHTML = 'No artifacts found. Upload some files to get started!'; + tbody.innerHTML = 'No artifacts found. Upload some files to get started!'; document.getElementById('artifact-count').textContent = '0 artifacts'; return; } tbody.innerHTML = artifacts.map(artifact => ` - ${artifact.id} + ${artifact.test_suite || '-'} - + ${escapeHtml(artifact.filename)} - ${artifact.file_type} - ${formatBytes(artifact.file_size)} + ${formatDate(artifact.created_at)} ${artifact.test_name || '-'} - ${artifact.test_suite || '-'} - ${formatResult(artifact.test_result)} - ${formatTags(artifact.tags)} - ${formatDate(artifact.created_at)}
@@ -82,6 +83,9 @@ function displayArtifacts(artifacts) { `).join(''); document.getElementById('artifact-count').textContent = `${artifacts.length} artifacts`; + + // Re-initialize Lucide icons for dynamically added content + lucide.createIcons(); } // Format result badge @@ -149,11 +153,11 @@ async function showDetail(id) {
${artifact.storage_path}
-
Test Name
+
Uploaded By
${artifact.test_name || '-'}
-
Test Suite
+
Sim Source
${artifact.test_suite || '-'}
@@ -198,15 +202,18 @@ async function showDetail(id) {
`; document.getElementById('detail-modal').classList.add('active'); + + // Re-initialize Lucide icons for modal content + lucide.createIcons(); } catch (error) { alert('Error loading artifact details: ' + error.message); } @@ -460,3 +467,40 @@ function nextPage() { currentPage++; loadArtifacts(pageSize, (currentPage - 1) * pageSize); } + +// Auto-refresh functions +function startAutoRefresh() { + if (autoRefreshInterval) { + clearInterval(autoRefreshInterval); + } + + if (autoRefreshEnabled) { + autoRefreshInterval = setInterval(() => { + // Only refresh if on the artifacts tab + const artifactsTab = document.getElementById('artifacts-tab'); + if (artifactsTab && artifactsTab.classList.contains('active')) { + loadArtifacts(pageSize, (currentPage - 1) * pageSize); + } + }, REFRESH_INTERVAL_MS); + } +} + +function toggleAutoRefresh() { + autoRefreshEnabled = !autoRefreshEnabled; + + const toggleBtn = document.getElementById('auto-refresh-toggle'); + if (autoRefreshEnabled) { + toggleBtn.textContent = 'Auto-refresh: ON'; + toggleBtn.classList.remove('btn-secondary'); + toggleBtn.classList.add('btn-success'); + startAutoRefresh(); + } else { + toggleBtn.textContent = 'Auto-refresh: OFF'; + toggleBtn.classList.remove('btn-success'); + toggleBtn.classList.add('btn-secondary'); + if (autoRefreshInterval) { + clearInterval(autoRefreshInterval); + autoRefreshInterval = null; + } + } +}