diff --git a/.gitignore b/.gitignore index 64db696..13e92a9 100644 --- a/.gitignore +++ b/.gitignore @@ -86,3 +86,10 @@ helm/charts/ tmp/ temp/ *.tmp + +# Node.js +package-lock.json +**/package-lock.json + +# Built static files (generated during Docker build from Angular) +static/ diff --git a/Dockerfile b/Dockerfile index 66de827..b12df05 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,3 +1,30 @@ +# Multi-stage build: First stage builds Angular frontend +FROM node:24-alpine AS frontend-build + +# Accept npm registry as build argument +ARG NPM_REGISTRY=https://registry.npmjs.org/ + +WORKDIR /frontend + +# Copy package files +COPY frontend/package*.json ./ + +# Configure npm registry if custom registry is provided +RUN if [ "$NPM_REGISTRY" != "https://registry.npmjs.org/" ]; then \ + echo "Using custom npm registry: $NPM_REGISTRY"; \ + npm config set registry "$NPM_REGISTRY"; \ + fi + +# Install dependencies (ignore package-lock.json if using custom registry) +RUN npm install + +# Copy source code +COPY frontend/ ./ + +# Build for production +RUN npm run build:prod + +# Second stage: Python backend with Angular frontend FROM python:3.11-alpine WORKDIR /app @@ -20,7 +47,9 @@ COPY app/ ./app/ COPY utils/ ./utils/ COPY alembic/ ./alembic/ COPY alembic.ini . -COPY static/ ./static/ + +# Copy built Angular frontend from first stage to static directory +COPY --from=frontend-build /frontend/dist/frontend/browser ./static/ # Create non-root user (Alpine uses adduser instead of useradd) RUN adduser -D -u 1000 appuser && chown -R appuser:appuser /app diff --git a/Dockerfile.frontend b/Dockerfile.frontend deleted file mode 100644 index 21ad07e..0000000 --- a/Dockerfile.frontend +++ /dev/null @@ -1,40 +0,0 @@ -# Multi-stage build for Angular frontend -FROM node:24-alpine AS build - -# Accept npm registry as build argument -ARG NPM_REGISTRY=https://registry.npmjs.org/ - -WORKDIR /app - -# Copy package files -COPY frontend/package*.json ./ - -# Configure npm registry and regenerate package-lock.json if custom registry is provided -RUN if [ "$NPM_REGISTRY" != "https://registry.npmjs.org/" ]; then \ - echo "Using custom npm registry: $NPM_REGISTRY"; \ - npm config set registry "$NPM_REGISTRY"; \ - rm -f package-lock.json; \ - npm install --package-lock-only; \ - fi - -# Install dependencies -RUN npm ci - -# Copy source code -COPY frontend/ ./ - -# Build for production -RUN npm run build:prod - -# Final stage - nginx to serve static files -FROM nginx:alpine - -# Copy built Angular app to nginx -COPY --from=build /app/dist/frontend/browser /usr/share/nginx/html - -# Copy nginx configuration -COPY nginx.conf /etc/nginx/conf.d/default.conf - -EXPOSE 80 - -CMD ["nginx", "-g", "daemon off;"] diff --git a/Dockerfile.frontend.prebuilt b/Dockerfile.frontend.prebuilt deleted file mode 100644 index f5599ce..0000000 --- a/Dockerfile.frontend.prebuilt +++ /dev/null @@ -1,20 +0,0 @@ -# Dockerfile for pre-built Angular frontend (air-gapped/restricted environments) -# -# IMPORTANT: You must build the Angular app BEFORE running docker-compose! -# Run this command first: ./build-for-airgap.sh -# OR manually: cd frontend && npm install && npm run build:prod -# -# This Dockerfile expects frontend/dist/frontend/browser to exist - -FROM nginx:alpine - -# Copy pre-built Angular app to nginx -# If this step fails, you need to run: ./build-for-airgap.sh -COPY frontend/dist/frontend/browser /usr/share/nginx/html - -# Copy nginx configuration -COPY nginx.conf /etc/nginx/conf.d/default.conf - -EXPOSE 80 - -CMD ["nginx", "-g", "daemon off;"] diff --git a/README.md b/README.md index b894398..2c37805 100644 --- a/README.md +++ b/README.md @@ -48,11 +48,6 @@ A lightweight, cloud-native API for storing and querying test artifacts includin .\quickstart.ps1 ``` -**Windows (Command Prompt):** -```batch -quickstart.bat -``` - ### Air-Gapped/Restricted Environment Deployment **For environments with restricted npm access:** @@ -65,7 +60,7 @@ This script: 2. Packages pre-built files into Docker 3. Starts all services -See [DEPLOYMENT.md](DEPLOYMENT.md) for detailed instructions. +See [DEPLOYMENT.md](docs/DEPLOYMENT.md) for detailed instructions. ### Manual Setup with Docker Compose diff --git a/app/main.py b/app/main.py index 6ac02e3..a6c252d 100644 --- a/app/main.py +++ b/app/main.py @@ -1,4 +1,4 @@ -from fastapi import FastAPI +from fastapi import FastAPI, Request from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles from fastapi.responses import FileResponse @@ -39,10 +39,8 @@ app.add_middleware( app.include_router(artifacts_router) app.include_router(seed_router) -# Mount static files +# Static directory setup static_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), "static") -if os.path.exists(static_dir): - app.mount("/static", StaticFiles(directory=static_dir), name="static") @app.on_event("startup") @@ -84,6 +82,30 @@ async def ui_root(): } +# Catch-all route for Angular SPA routing - must be last +@app.get("/{full_path:path}") +async def serve_spa(full_path: str): + """Serve Angular SPA static files and handle client-side routing""" + # Try to serve static file first (JS, CSS, images, etc.) + file_path = os.path.join(static_dir, full_path) + if os.path.exists(file_path) and os.path.isfile(file_path): + return FileResponse(file_path) + + # For all other routes (Angular client-side routes), serve index.html + index_path = os.path.join(static_dir, "index.html") + if os.path.exists(index_path): + return FileResponse(index_path) + else: + return { + "message": "Warehouse13 - Enterprise Test Artifact Storage", + "version": "1.0.0", + "docs": "/docs", + "ui": "UI not found. Serving API only.", + "deployment_mode": settings.deployment_mode, + "storage_backend": settings.storage_backend + } + + @app.get("/health") async def health_check(): """Health check endpoint""" diff --git a/build-for-airgap.sh b/build-for-airgap.sh old mode 100755 new mode 100644 index d91cf77..6279808 --- a/build-for-airgap.sh +++ b/build-for-airgap.sh @@ -15,7 +15,7 @@ fi # Check if node is installed if ! command -v node &> /dev/null; then - echo "Error: Node.js is not installed. Please install Node.js 18+ first." + echo "Error: Node.js is not installed. Please install Node.js 24+ first." exit 1 fi @@ -34,30 +34,37 @@ echo "Step 2/3: Building Angular production bundle..." npm run build:prod echo "" -echo "Step 3/3: Verifying build output..." +echo "Step 3/3: Copying to static directory..." if [ -d "dist/frontend/browser" ]; then echo "✓ Build successful!" echo "✓ Output: frontend/dist/frontend/browser" - ls -lh dist/frontend/browser | head -5 + + # Copy to static directory for local FastAPI serving + cd .. + rm -rf static/* + cp -r frontend/dist/frontend/browser/* static/ + echo "✓ Copied to static/ directory" + + ls -lh static/ | head -10 else echo "✗ Build failed - output directory not found" exit 1 fi -cd .. - echo "" echo "=========================================" echo "Build Complete!" echo "=========================================" echo "" -echo "Next steps:" -echo "1. Update docker-compose.yml:" -echo " Change: dockerfile: Dockerfile.frontend" -echo " To: dockerfile: Dockerfile.frontend.prebuilt" +echo "The Angular app has been built and copied to static/" +echo "You can now:" echo "" -echo "2. Deploy:" +echo "1. Run locally with FastAPI:" +echo " uvicorn app.main:app --reload" +echo " Access at: http://localhost:8000" +echo "" +echo "2. Deploy with Docker:" echo " docker-compose up -d --build" +echo " (Docker will rebuild Angular during build)" echo "" -echo "See DEPLOYMENT.md for more details." echo "=========================================" diff --git a/docker-compose.yml b/docker-compose.yml index 8a2f2d1..0fdcc5c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -36,7 +36,8 @@ services: timeout: 5s retries: 5 - api: + app: + container_name: warehouse13-app build: . ports: - "8000:8000" @@ -59,20 +60,6 @@ services: timeout: 10s retries: 3 - frontend: - build: - context: . - dockerfile: Dockerfile.frontend.prebuilt - ports: - - "4200:80" - depends_on: - - api - healthcheck: - test: ["CMD", "wget", "-q", "--spider", "http://localhost/"] - interval: 30s - timeout: 10s - retries: 3 - volumes: postgres_data: minio_data: diff --git a/API.md b/docs/API.md similarity index 100% rename from API.md rename to docs/API.md diff --git a/ARCHITECTURE.md b/docs/ARCHITECTURE.md similarity index 100% rename from ARCHITECTURE.md rename to docs/ARCHITECTURE.md diff --git a/DEPLOYMENT.md b/docs/DEPLOYMENT.md similarity index 100% rename from DEPLOYMENT.md rename to docs/DEPLOYMENT.md diff --git a/FEATURES.md b/docs/FEATURES.md similarity index 100% rename from FEATURES.md rename to docs/FEATURES.md diff --git a/FRONTEND_SETUP.md b/docs/FRONTEND_SETUP.md similarity index 100% rename from FRONTEND_SETUP.md rename to docs/FRONTEND_SETUP.md diff --git a/SUMMARY.md b/docs/SUMMARY.md similarity index 100% rename from SUMMARY.md rename to docs/SUMMARY.md diff --git a/frontend/angular.json b/frontend/angular.json index 5784175..919a41c 100644 --- a/frontend/angular.json +++ b/frontend/angular.json @@ -93,5 +93,8 @@ } } } + }, + "cli": { + "analytics": false } } diff --git a/nginx.conf b/nginx.conf deleted file mode 100644 index bcc9963..0000000 --- a/nginx.conf +++ /dev/null @@ -1,36 +0,0 @@ -server { - listen 80; - server_name localhost; - root /usr/share/nginx/html; - index index.html; - - # Gzip compression - gzip on; - gzip_vary on; - gzip_min_length 1024; - gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/json; - - # Angular routes - serve index.html for all routes - location / { - try_files $uri $uri/ /index.html; - } - - # Proxy API requests to backend - location /api { - proxy_pass http://api:8000; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection 'upgrade'; - proxy_set_header Host $host; - proxy_cache_bypass $http_upgrade; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - # Cache static assets - location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { - expires 1y; - add_header Cache-Control "public, immutable"; - } -} diff --git a/quickstart-airgap.sh b/quickstart-airgap.sh index a726a32..0f6097f 100755 --- a/quickstart-airgap.sh +++ b/quickstart-airgap.sh @@ -46,8 +46,7 @@ echo "=========================================" echo "Services are running!" echo "=========================================" echo "" -echo "Frontend: http://localhost:4200" -echo "API: http://localhost:8000" +echo "Web UI: http://localhost:8000" echo "API Docs: http://localhost:8000/docs" echo "MinIO Console: http://localhost:9001" echo " Username: minioadmin" diff --git a/quickstart.bat b/quickstart.bat deleted file mode 100644 index d0f8808..0000000 --- a/quickstart.bat +++ /dev/null @@ -1,106 +0,0 @@ -@echo off -setlocal enabledelayedexpansion - -echo ========================================= -echo Warehouse13 - Quick Start -echo ========================================= -echo. - -REM Check if Docker is installed -where docker >nul 2>nul -if %errorlevel% neq 0 ( - echo Error: Docker is not installed. Please install Docker Desktop first. - echo Visit: https://www.docker.com/products/docker-desktop - pause - exit /b 1 -) - -REM Check if Docker Compose is available -where docker-compose >nul 2>nul -if %errorlevel% neq 0 ( - REM Try docker compose (new version) - docker compose version >nul 2>nul - if %errorlevel% neq 0 ( - echo Error: Docker Compose is not available. - echo Please ensure Docker Desktop is running. - pause - exit /b 1 - ) - set COMPOSE_CMD=docker compose -) else ( - set COMPOSE_CMD=docker-compose -) - -REM Create .env file if it doesn't exist -if not exist .env ( - echo Creating .env file from .env.example... - copy .env.example .env >nul - echo [OK] .env file created -) else ( - echo [OK] .env file already exists -) - -echo. -echo Building and starting services with Docker Compose... -%COMPOSE_CMD% up -d --build - -if %errorlevel% neq 0 ( - echo. - echo Error: Failed to start services. - echo Make sure Docker Desktop is running. - pause - exit /b 1 -) - -echo. -echo Waiting for services to be ready... -timeout /t 15 /nobreak >nul - -echo. -echo ========================================= -echo Services are running! -echo ========================================= -echo. -echo Web UI: http://localhost:8000 -echo API Docs: http://localhost:8000/docs -echo MinIO Console: http://localhost:9001 -echo Username: minioadmin -echo Password: minioadmin -echo. -echo To view logs: %COMPOSE_CMD% logs -f -echo To stop: %COMPOSE_CMD% down -echo. -echo ========================================= -echo Testing the API... -echo ========================================= -echo. - -REM Wait a bit more for API to be fully ready -timeout /t 5 /nobreak >nul - -REM Test health endpoint -curl -s http://localhost:8000/health | findstr "healthy" >nul 2>nul -if %errorlevel% equ 0 ( - echo [OK] API is healthy! - echo. - echo ========================================= - echo Open your browser to get started: - echo http://localhost:8000 - echo ========================================= -) else ( - echo [WARNING] API is not responding yet. - echo Please wait a moment and check http://localhost:8000 -) - -echo. -echo ========================================= -echo Setup complete! -echo ========================================= -echo. -echo Press any key to open the UI in your browser... -pause >nul - -REM Open browser -start http://localhost:8000 - -exit /b 0 diff --git a/quickstart.ps1 b/quickstart.ps1 index 920c584..03804f0 100644 --- a/quickstart.ps1 +++ b/quickstart.ps1 @@ -121,7 +121,7 @@ Write-Host "Useful Commands:" -ForegroundColor Cyan Write-Host " Generate seed data: " -NoNewline Write-Host "Use the 'Generate Seed Data' button in the UI" -ForegroundColor Yellow Write-Host " View logs: " -NoNewline -Write-Host "$composeCmd logs -f api" -ForegroundColor Yellow +Write-Host "$composeCmd logs -f app" -ForegroundColor Yellow Write-Host " Restart services: " -NoNewline Write-Host "$composeCmd restart" -ForegroundColor Yellow Write-Host " Stop all: " -NoNewline diff --git a/quickstart.sh b/quickstart.sh index 3bbafca..591d37c 100755 --- a/quickstart.sh +++ b/quickstart.sh @@ -41,7 +41,7 @@ echo "=========================================" echo "Services are running!" echo "=========================================" echo "" -echo "API: http://localhost:8000" +echo "Web UI: http://localhost:8000" echo "API Docs: http://localhost:8000/docs" echo "MinIO Console: http://localhost:9001" echo " Username: minioadmin" diff --git a/static/css/styles.css b/static/css/styles.css deleted file mode 100644 index edee773..0000000 --- a/static/css/styles.css +++ /dev/null @@ -1,564 +0,0 @@ -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; - background: #0f172a; - min-height: 100vh; - padding: 20px; - color: #e2e8f0; -} - -.container { - max-width: 1400px; - margin: 0 auto; - background: #1e293b; - border-radius: 12px; - box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5); - overflow: hidden; -} - -header { - background: linear-gradient(135deg, #1e3a8a 0%, #4338ca 100%); - color: white; - padding: 30px; - display: flex; - justify-content: space-between; - align-items: center; -} - -header h1 { - font-size: 28px; - font-weight: 600; - display: flex; - align-items: center; - gap: 12px; -} - -.logo { - font-family: 'Courier New', monospace; - font-weight: 700; - font-size: 24px; - color: #60a5fa; - letter-spacing: -1px; - padding: 2px 4px; - border: 2px solid #60a5fa; - border-radius: 4px; - background: rgba(96, 165, 250, 0.1); -} - -.header-info { - display: flex; - gap: 10px; -} - -.badge { - background: rgba(255, 255, 255, 0.2); - padding: 6px 12px; - border-radius: 20px; - font-size: 12px; - font-weight: 500; - text-transform: uppercase; - backdrop-filter: blur(10px); -} - -.tabs { - display: flex; - background: #0f172a; - border-bottom: 2px solid #334155; -} - -.tab-button { - flex: 1; - padding: 16px 24px; - background: none; - border: none; - font-size: 14px; - font-weight: 500; - cursor: pointer; - transition: all 0.3s; - color: #94a3b8; - display: inline-flex; - align-items: center; - justify-content: center; - gap: 8px; -} - -.tab-button:hover { - background: #1e293b; - color: #e2e8f0; -} - -.tab-button.active { - background: #1e293b; - color: #60a5fa; - border-bottom: 3px solid #60a5fa; -} - -.tab-content { - display: none; - padding: 30px; -} - -.tab-content.active { - display: block; -} - -.toolbar { - display: flex; - gap: 10px; - margin-bottom: 20px; - align-items: center; -} - -.filter-inline { - display: flex; - align-items: center; - gap: 8px; - padding: 8px 12px; - background: #0f172a; - border-radius: 6px; - border: 1px solid #334155; - min-width: 250px; -} - -.filter-inline input { - flex: 1; - padding: 4px 8px; - background: transparent; - border: none; - color: #e2e8f0; - font-size: 14px; -} - -.filter-inline input:focus { - outline: none; -} - -.filter-inline input::placeholder { - color: #64748b; -} - -.btn-clear { - background: none; - border: none; - cursor: pointer; - padding: 4px; - border-radius: 4px; - color: #64748b; - display: flex; - align-items: center; - justify-content: center; - transition: all 0.3s; -} - -.btn-clear:hover { - background: #334155; - color: #e2e8f0; -} - -.btn { - padding: 10px 20px; - border: none; - border-radius: 6px; - font-size: 14px; - font-weight: 500; - cursor: pointer; - transition: all 0.3s; - display: inline-flex; - align-items: center; - gap: 8px; -} - -.btn-primary { - background: #3b82f6; - color: white; -} - -.btn-primary:hover { - background: #2563eb; - transform: translateY(-1px); -} - -.btn-secondary { - background: #334155; - color: #e2e8f0; -} - -.btn-secondary:hover { - background: #475569; -} - -.btn-danger { - background: #ef4444; - color: white; -} - -.btn-danger:hover { - background: #dc2626; -} - -.btn-success { - background: #10b981; - color: white; -} - -.btn-large { - padding: 14px 28px; - font-size: 16px; -} - -.count-badge { - background: #1e3a8a; - color: #93c5fd; - padding: 8px 16px; - border-radius: 20px; - font-size: 13px; - font-weight: 600; - margin-left: auto; -} - -.table-container { - overflow-x: auto; - border: 1px solid #334155; - border-radius: 8px; - background: #0f172a; -} - -table { - width: 100%; - border-collapse: collapse; - font-size: 14px; -} - -thead { - background: #1e293b; -} - -th { - padding: 14px 12px; - text-align: left; - font-weight: 600; - color: #94a3b8; - border-bottom: 2px solid #334155; - white-space: nowrap; - text-transform: uppercase; - font-size: 12px; - letter-spacing: 0.5px; -} - -th.sortable { - cursor: pointer; - user-select: none; - transition: color 0.3s; -} - -th.sortable:hover { - color: #60a5fa; -} - -.sort-indicator { - display: inline-block; - margin-left: 5px; - font-size: 10px; - color: #64748b; -} - -th.sort-asc .sort-indicator::after { - content: '▲'; - color: #60a5fa; -} - -th.sort-desc .sort-indicator::after { - content: '▼'; - color: #60a5fa; -} - -td { - padding: 16px 12px; - border-bottom: 1px solid #1e293b; - color: #cbd5e1; -} - -tbody tr:hover { - background: #1e293b; -} - -.loading { - text-align: center; - color: #64748b; - padding: 40px !important; -} - -.result-badge { - padding: 4px 10px; - border-radius: 12px; - font-size: 12px; - font-weight: 600; - text-transform: uppercase; -} - -.result-pass { - background: #064e3b; - color: #6ee7b7; -} - -.result-fail { - background: #7f1d1d; - color: #fca5a5; -} - -.result-skip { - background: #78350f; - color: #fcd34d; -} - -.result-error { - background: #7f1d1d; - color: #fca5a5; -} - -.tag { - display: inline-block; - background: #1e3a8a; - color: #93c5fd; - padding: 3px 8px; - border-radius: 10px; - font-size: 11px; - margin: 2px; -} - -.file-type-badge { - background: #1e3a8a; - color: #93c5fd; - padding: 4px 8px; - border-radius: 6px; - font-size: 11px; - font-weight: 600; - text-transform: uppercase; -} - -.pagination { - display: flex; - justify-content: center; - align-items: center; - gap: 20px; - margin-top: 20px; - padding: 20px; -} - -#page-info { - font-weight: 500; - color: #94a3b8; -} - -.upload-section, .query-section { - max-width: 800px; - margin: 0 auto; -} - -.form-group { - margin-bottom: 20px; -} - -.form-row { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 20px; -} - -label { - display: block; - font-weight: 500; - color: #cbd5e1; - margin-bottom: 6px; - font-size: 14px; -} - -input[type="text"], -input[type="file"], -input[type="datetime-local"], -select, -textarea { - width: 100%; - padding: 10px 14px; - 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: #3b82f6; - box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); -} - -small { - color: #64748b; - font-size: 12px; - display: block; - margin-top: 4px; -} - -#upload-status { - margin-top: 20px; - padding: 14px; - border-radius: 6px; - display: none; -} - -#upload-status.success { - background: #064e3b; - color: #6ee7b7; - display: block; -} - -#upload-status.error { - background: #7f1d1d; - color: #fca5a5; - display: block; -} - -.modal { - display: none; - position: fixed; - z-index: 1000; - left: 0; - top: 0; - width: 100%; - height: 100%; - background: rgba(0, 0, 0, 0.8); - backdrop-filter: blur(4px); -} - -.modal.active { - display: flex; - align-items: center; - justify-content: center; -} - -.modal-content { - 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.5); - border: 1px solid #334155; -} - -.close { - position: absolute; - right: 20px; - top: 20px; - font-size: 28px; - font-weight: bold; - color: #64748b; - cursor: pointer; - transition: color 0.3s; -} - -.close:hover { - color: #e2e8f0; -} - -.detail-row { - margin-bottom: 16px; - padding-bottom: 16px; - border-bottom: 1px solid #334155; -} - -.detail-row:last-child { - border-bottom: none; -} - -.detail-label { - font-weight: 600; - color: #94a3b8; - margin-bottom: 4px; -} - -.detail-value { - color: #cbd5e1; -} - -pre { - 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 { - display: flex; - gap: 8px; -} - -.icon-btn { - background: none; - border: none; - cursor: pointer; - padding: 8px; - border-radius: 4px; - transition: all 0.3s; - color: #94a3b8; - display: inline-flex; - align-items: center; - justify-content: center; -} - -.icon-btn:hover { - background: #334155; - color: #e2e8f0; - transform: scale(1.1); -} - -/* Ensure SVG icons inherit color */ -.icon-btn svg { - stroke: currentColor; -} - -@media (max-width: 768px) { - .form-row { - grid-template-columns: 1fr; - } - - header { - flex-direction: column; - gap: 15px; - text-align: center; - } - - .table-container { - font-size: 12px; - } - - th, td { - padding: 8px 6px; - } - - .toolbar { - flex-wrap: wrap; - } -} diff --git a/static/index.html b/static/index.html deleted file mode 100644 index a41a8ad..0000000 --- a/static/index.html +++ /dev/null @@ -1,251 +0,0 @@ - - -
- - -| - Sim Source - | -- Artifacts - | -- Date - | -- Uploaded By - | -Actions | -
|---|---|---|---|---|
| Loading artifacts... | -||||
${artifact.storage_path}${JSON.stringify(artifact.test_config, null, 2)}${JSON.stringify(artifact.custom_metadata, null, 2)}