← back to Watches

public/pwa-index.html

437 lines

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, user-scalable=yes">
    <meta name="mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
    <meta name="apple-mobile-web-app-title" content="Omega Watches">
    <meta name="theme-color" content="#C41E3A">
    <meta name="description" content="Track historical prices and analytics for Omega luxury timepieces. AR try-on, camera scanner, and offline support.">

    <title>Omega Watch Price History - PWA</title>

    <!-- Manifest -->
    <link rel="manifest" href="/manifest.json">

    <!-- Icons -->
    <link rel="icon" type="image/svg+xml" href="/icons/icon-72x72.svg">
    <link rel="apple-touch-icon" href="/icons/icon-192x192.svg">

    <!-- Fonts -->
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">

    <!-- Styles -->
    <link rel="stylesheet" href="pwa-styles.css">

    <style>
        /* Critical CSS */
        body {
            margin: 0;
            padding: 0;
            font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
            background: #0C1E3C;
            color: #fff;
            overflow-x: hidden;
        }

        .loading-screen {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: linear-gradient(135deg, #0C1E3C 0%, #1a2f50 100%);
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            z-index: 9999;
        }

        .loading-logo {
            font-size: 72px;
            margin-bottom: 20px;
            animation: pulse 2s infinite;
        }

        @keyframes pulse {
            0%, 100% { opacity: 1; transform: scale(1); }
            50% { opacity: 0.7; transform: scale(1.05); }
        }

        .loading-text {
            font-size: 18px;
            color: #C41E3A;
            font-weight: 600;
        }
    </style>
</head>
<body>
    <!-- Loading Screen -->
    <div class="loading-screen" id="loadingScreen">
        <div class="loading-logo">Ω</div>
        <div class="loading-text">Loading Omega Watches...</div>
    </div>

    <!-- Update Banner -->
    <div id="update-banner" class="update-banner" style="display: none;">
        <div class="update-content">
            <span>A new version is available!</span>
            <button id="update-button" class="btn-update">Update Now</button>
        </div>
    </div>

    <!-- Install Banner -->
    <div id="install-banner" class="install-banner" style="display: none;">
        <div class="install-content">
            <div class="install-icon">Ω</div>
            <div class="install-text">
                <strong>Install Omega Watches App</strong>
                <p>Get the full experience with offline access and AR try-on</p>
            </div>
            <button id="install-button" class="btn-install">Install</button>
            <button id="install-dismiss" class="btn-dismiss">×</button>
        </div>
    </div>

    <!-- Offline Indicator -->
    <div id="offline-indicator" class="offline-indicator" style="display: none;">
        <svg width="16" height="16" fill="currentColor">
            <circle cx="8" cy="8" r="7" stroke="currentColor" fill="none" stroke-width="2"/>
            <line x1="4" y1="8" x2="12" y2="8" stroke="currentColor" stroke-width="2"/>
        </svg>
        <span>Offline Mode</span>
    </div>

    <!-- Main App Container -->
    <div id="app" class="app-container">
        <!-- Navigation -->
        <nav class="main-nav">
            <div class="nav-brand">
                <span class="nav-logo">Ω</span>
                <span class="nav-title">Omega Watches</span>
            </div>

            <div class="nav-actions">
                <button id="biometric-login" class="btn-icon" style="display: none;" title="Biometric Login">
                    <svg width="24" height="24" fill="currentColor">
                        <path d="M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4zm0 6c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm0 14c-2.67 0-8-1.34-8-4v-1c0-2.66 5.33-4 8-4s8 1.34 8 4v1c0 2.66-5.33 4-8 4z"/>
                    </svg>
                </button>

                <button id="notification-toggle" class="btn-icon" title="Enable Notifications">
                    <svg width="24" height="24" fill="currentColor">
                        <path d="M12 22c1.1 0 2-.9 2-2h-4c0 1.1.9 2 2 2zm6-6v-5c0-3.07-1.63-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.64 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2zm-2 1H8v-6c0-2.48 1.51-4.5 4-4.5s4 2.02 4 4.5v6z"/>
                    </svg>
                </button>

                <button id="menu-toggle" class="btn-icon" title="Menu">
                    <svg width="24" height="24" fill="currentColor">
                        <path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"/>
                    </svg>
                </button>
            </div>
        </nav>

        <!-- Bottom Navigation -->
        <div class="bottom-nav">
            <button class="nav-item active" data-view="dashboard">
                <svg width="24" height="24" fill="currentColor">
                    <path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/>
                </svg>
                <span>Home</span>
            </button>

            <button class="nav-item" data-view="scanner">
                <svg width="24" height="24" fill="currentColor">
                    <path d="M9.5 6.5v3h-3v-3h3M11 5H5v6h6V5zm-1.5 9.5v3h-3v-3h3M11 13H5v6h6v-6zm6.5-6.5v3h-3v-3h3M19 5h-6v6h6V5zm-6 8h1.5v1.5H13V13zm1.5 1.5H16V16h-1.5v-1.5zM16 13h1.5v1.5H16V13zm-3 3h1.5v1.5H13V16zm1.5 1.5H16V19h-1.5v-1.5zM16 16h1.5v1.5H16V16zm1.5-1.5H19V16h-1.5v-1.5zm0 3H19V19h-1.5v-1.5zM22 7h-2V4h-3V2h5v5zm0 15v-5h-2v3h-3v2h5zM2 22h5v-2H4v-3H2v5zM2 2v5h2V4h3V2H2z"/>
                </svg>
                <span>Scan</span>
            </button>

            <button class="nav-item" data-view="ar">
                <svg width="24" height="24" fill="currentColor">
                    <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>
                </svg>
                <span>AR Try-On</span>
            </button>

            <button class="nav-item" data-view="favorites">
                <svg width="24" height="24" fill="currentColor">
                    <path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/>
                </svg>
                <span>Favorites</span>
            </button>
        </div>

        <!-- Main Content Views -->
        <main class="main-content">
            <!-- Dashboard View -->
            <div id="dashboard-view" class="view active">
                <div class="container">
                    <header class="page-header">
                        <h1>Omega Watch Price History</h1>
                        <p class="subtitle">Complete Historical Price Tracking</p>
                    </header>

                    <div class="stats-grid" id="statsGrid">
                        <div class="stat-card">
                            <div class="stat-value" id="totalModels">--</div>
                            <div class="stat-label">Total Models</div>
                        </div>
                        <div class="stat-card">
                            <div class="stat-value" id="avgPrice">--</div>
                            <div class="stat-label">Avg. Price</div>
                        </div>
                        <div class="stat-card">
                            <div class="stat-value" id="topAppreciation">--</div>
                            <div class="stat-label">Top Appreciation</div>
                        </div>
                        <div class="stat-card">
                            <div class="stat-value" id="dataPoints">--</div>
                            <div class="stat-label">Data Points</div>
                        </div>
                    </div>

                    <div class="watches-list" id="watchesList">
                        <!-- Watches loaded dynamically -->
                    </div>
                </div>
            </div>

            <!-- Scanner View -->
            <div id="scanner-view" class="view">
                <div class="scanner-container">
                    <div class="scanner-header">
                        <h2>Scan Watch Reference</h2>
                        <button id="scanner-close" class="btn-close">×</button>
                    </div>

                    <div class="scanner-viewport">
                        <video id="scanner-video" autoplay playsinline></video>
                        <canvas id="scanner-canvas" style="display: none;"></canvas>

                        <div class="scanner-overlay">
                            <div class="scan-frame"></div>
                            <p class="scan-instructions">Position watch reference number in the frame</p>
                        </div>
                    </div>

                    <div class="scanner-controls">
                        <button id="scanner-capture" class="btn-primary">
                            <svg width="24" height="24" fill="currentColor">
                                <circle cx="12" cy="12" r="8" stroke="currentColor" fill="none" stroke-width="2"/>
                                <circle cx="12" cy="12" r="5" fill="currentColor"/>
                            </svg>
                            Capture
                        </button>

                        <button id="scanner-switch" class="btn-secondary">
                            <svg width="24" height="24" fill="currentColor">
                                <path d="M20 4h-3.17L15 2H9L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zM9 4l.83 1h4.34l.83-1h2v2H7V4h2z"/>
                            </svg>
                            Flip
                        </button>

                        <button id="scanner-torch" class="btn-secondary">
                            <svg width="24" height="24" fill="currentColor">
                                <path d="M6 14l3 3v5h6v-5l3-3V9H6v5zm5-12h2v3h-2V2zM3.5 5.88l1.41-1.41 2.12 2.12L5.62 8 3.5 5.88zm13.46.71l2.12-2.12 1.41 1.41L18.38 8l-1.42-1.41z"/>
                            </svg>
                        </button>
                    </div>

                    <div class="scan-history" id="scanHistory">
                        <h3>Recent Scans</h3>
                        <div id="scanHistoryList"></div>
                    </div>
                </div>
            </div>

            <!-- AR View -->
            <div id="ar-view" class="view">
                <div class="ar-container">
                    <div class="ar-header">
                        <h2>AR Try-On</h2>
                        <button id="ar-close" class="btn-close">×</button>
                    </div>

                    <div class="ar-viewport">
                        <video id="ar-video" autoplay playsinline></video>
                        <canvas id="ar-canvas"></canvas>

                        <div class="ar-controls-overlay">
                            <button id="ar-screenshot" class="btn-icon ar-btn">
                                <svg width="24" height="24" fill="currentColor">
                                    <circle cx="12" cy="12" r="3.2"/>
                                    <path d="M9 2L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2h-3.17L15 2H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"/>
                                </svg>
                            </button>
                        </div>
                    </div>

                    <div class="ar-watch-selector">
                        <h3>Select Watch Model</h3>
                        <div id="arWatchList" class="ar-watch-grid">
                            <!-- Populated dynamically -->
                        </div>
                    </div>
                </div>
            </div>

            <!-- Favorites View -->
            <div id="favorites-view" class="view">
                <div class="container">
                    <header class="page-header">
                        <h1>Favorite Watches</h1>
                        <p class="subtitle">Your saved collection</p>
                    </header>

                    <div id="favoritesList" class="watches-list">
                        <div class="empty-state">
                            <svg width="64" height="64" fill="currentColor" opacity="0.3">
                                <path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/>
                            </svg>
                            <p>No favorites yet</p>
                            <p class="empty-state-hint">Tap the heart icon on any watch to add it here</p>
                        </div>
                    </div>
                </div>
            </div>
        </main>
    </div>

    <!-- PWA Scripts -->
    <script src="biometric-auth.js"></script>
    <script src="camera-scanner.js"></script>
    <script src="ar-viewer.js"></script>
    <script src="pwa-app.js"></script>
    <script src="app.js"></script>

    <script>
        // Hide loading screen when app is ready
        window.addEventListener('load', () => {
            setTimeout(() => {
                document.getElementById('loadingScreen').style.display = 'none';
            }, 1000);
        });

        // Handle navigation
        document.querySelectorAll('.nav-item').forEach(btn => {
            btn.addEventListener('click', () => {
                const view = btn.dataset.view;

                // Update active nav
                document.querySelectorAll('.nav-item').forEach(b => b.classList.remove('active'));
                btn.classList.add('active');

                // Show view
                document.querySelectorAll('.view').forEach(v => v.classList.remove('active'));
                document.getElementById(`${view}-view`).classList.add('active');

                // Initialize view-specific functionality
                if (view === 'scanner') {
                    initScanner();
                } else if (view === 'ar') {
                    initAR();
                }
            });
        });

        // Scanner initialization
        async function initScanner() {
            try {
                const scanner = await window.pwaApp.initScanner();
                await scanner.start();

                document.getElementById('scanner-capture').addEventListener('click', async () => {
                    const imageData = scanner.captureFrame();
                    // Process image...
                });

                document.getElementById('scanner-switch').addEventListener('click', () => {
                    scanner.switchCamera();
                });

                document.getElementById('scanner-torch').addEventListener('click', () => {
                    scanner.toggleTorch();
                });
            } catch (error) {
                console.error('Scanner init failed:', error);
                alert('Camera not available: ' + error.message);
            }
        }

        // AR initialization
        async function initAR() {
            try {
                const ar = await window.pwaApp.initAR();

                // Load watch list for AR
                const response = await fetch('/api/watches?limit=10');
                const data = await response.json();

                const arWatchList = document.getElementById('arWatchList');
                arWatchList.innerHTML = data.watches.map(watch => `
                    <div class="ar-watch-card" data-id="${watch.id}">
                        <img src="/images/watches/${watch.id}.jpg" alt="${watch.model}"
                             onerror="this.src='data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 width=%22100%22 height=%22100%22><rect fill=%22%23333%22 width=%22100%22 height=%22100%22/></svg>'">
                        <p>${watch.model}</p>
                    </div>
                `).join('');

                // Handle watch selection
                document.querySelectorAll('.ar-watch-card').forEach(card => {
                    card.addEventListener('click', async () => {
                        await ar.start(card.dataset.id);
                    });
                });

                // Screenshot button
                document.getElementById('ar-screenshot').addEventListener('click', async () => {
                    const imageData = await ar.takeScreenshot();
                    if (imageData) {
                        alert('Screenshot saved!');
                    }
                });
            } catch (error) {
                console.error('AR init failed:', error);
                alert('AR not available: ' + error.message);
            }
        }

        // Notification toggle
        document.getElementById('notification-toggle').addEventListener('click', async () => {
            const granted = await window.pwaApp.requestNotificationPermission();
            if (granted) {
                alert('Notifications enabled! You will receive price alerts.');
            }
        });

        // Install button
        document.getElementById('install-dismiss').addEventListener('click', () => {
            document.getElementById('install-banner').style.display = 'none';
        });

        // Show install banner after a few seconds
        setTimeout(() => {
            if (window.pwaApp && window.pwaApp.installPrompt) {
                document.getElementById('install-banner').style.display = 'block';
            }
        }, 5000);

        // Offline indicator
        window.addEventListener('offline', () => {
            document.getElementById('offline-indicator').style.display = 'flex';
        });

        window.addEventListener('online', () => {
            document.getElementById('offline-indicator').style.display = 'none';
        });
    </script>
</body>
</html>