Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Latest Odoo Version Checker</title> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| :root { | |
| --primary-color: #714B67; | |
| --secondary-color: #F5E6E8; | |
| --accent-color: #A076F9; | |
| --text-color: #333; | |
| --light-text: #666; | |
| --shadow: 0 4px 6px rgba(0, 0, 0, 0.1); | |
| --border-radius: 12px; | |
| } | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
| } | |
| body { | |
| background-color: var(--secondary-color); | |
| color: var(--text-color); | |
| line-height: 1.6; | |
| min-height: 100vh; | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| header { | |
| background: linear-gradient(135deg, var(--primary-color), var(--accent-color)); | |
| color: white; | |
| padding: 1.5rem; | |
| text-align: center; | |
| box-shadow: var(--shadow); | |
| } | |
| .container { | |
| max-width: 800px; | |
| margin: 2rem auto; | |
| padding: 0 1rem; | |
| flex: 1; | |
| } | |
| .version-card { | |
| background: white; | |
| border-radius: var(--border-radius); | |
| padding: 2rem; | |
| box-shadow: var(--shadow); | |
| text-align: center; | |
| margin-bottom: 2rem; | |
| transition: transform 0.3s ease, box-shadow 0.3s ease; | |
| } | |
| .version-card:hover { | |
| transform: translateY(-5px); | |
| box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15); | |
| } | |
| .version-number { | |
| font-size: 3rem; | |
| font-weight: 700; | |
| margin: 1rem 0; | |
| color: var(--primary-color); | |
| background: linear-gradient(135deg, var(--primary-color), var(--accent-color)); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| background-clip: text; | |
| } | |
| .version-info { | |
| display: flex; | |
| justify-content: space-around; | |
| margin: 2rem 0; | |
| flex-wrap: wrap; | |
| gap: 1rem; | |
| } | |
| .info-item { | |
| text-align: center; | |
| padding: 1rem; | |
| background: rgba(113, 75, 103, 0.1); | |
| border-radius: var(--border-radius); | |
| flex: 1; | |
| min-width: 150px; | |
| } | |
| .info-item i { | |
| font-size: 2rem; | |
| color: var(--accent-color); | |
| margin-bottom: 0.5rem; | |
| } | |
| .loading { | |
| display: inline-block; | |
| width: 50px; | |
| height: 50px; | |
| border: 5px solid rgba(255, 255, 255, 0.3); | |
| border-radius: 50%; | |
| border-top-color: white; | |
| animation: spin 1s ease-in-out infinite; | |
| } | |
| @keyframes spin { | |
| to { transform: rotate(360deg); } | |
| } | |
| .btn { | |
| display: inline-block; | |
| background: var(--primary-color); | |
| color: white; | |
| padding: 0.8rem 1.5rem; | |
| border-radius: var(--border-radius); | |
| text-decoration: none; | |
| font-weight: 600; | |
| transition: all 0.3s ease; | |
| border: none; | |
| cursor: pointer; | |
| margin-top: 1rem; | |
| } | |
| .btn:hover { | |
| background: var(--accent-color); | |
| transform: translateY(-2px); | |
| } | |
| .error { | |
| color: #e74c3c; | |
| font-weight: 600; | |
| } | |
| footer { | |
| text-align: center; | |
| padding: 1.5rem; | |
| background: white; | |
| color: var(--light-text); | |
| font-size: 0.9rem; | |
| } | |
| .anycoder-link { | |
| color: var(--primary-color); | |
| text-decoration: none; | |
| font-weight: 600; | |
| margin-left: 0.5rem; | |
| } | |
| .anycoder-link:hover { | |
| text-decoration: underline; | |
| } | |
| .release-notes { | |
| background: white; | |
| border-radius: var(--border-radius); | |
| padding: 1.5rem; | |
| box-shadow: var(--shadow); | |
| margin-top: 2rem; | |
| } | |
| .release-notes h3 { | |
| margin-bottom: 1rem; | |
| color: var(--primary-color); | |
| } | |
| .release-notes ul { | |
| list-style-type: none; | |
| padding-left: 0; | |
| } | |
| .release-notes li { | |
| padding: 0.5rem 0; | |
| border-bottom: 1px solid #eee; | |
| } | |
| .release-notes li:last-child { | |
| border-bottom: none; | |
| } | |
| @media (max-width: 768px) { | |
| .version-number { | |
| font-size: 2.5rem; | |
| } | |
| .version-info { | |
| flex-direction: column; | |
| } | |
| } | |
| @media (max-width: 480px) { | |
| .container { | |
| padding: 0 0.5rem; | |
| } | |
| .version-card { | |
| padding: 1.5rem; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <header> | |
| <h1><i class="fab fa-odoo"></i> Odoo Version Checker</h1> | |
| <p>Built with <a href="https://huggingface.co/spaces/akhaliq/anycoder" class="anycoder-link" target="_blank">anycoder</a></p> | |
| </header> | |
| <div class="container"> | |
| <div class="version-card"> | |
| <h2>Latest Odoo Version</h2> | |
| <div id="version-display"> | |
| <div class="loading"></div> | |
| <p>Fetching latest version...</p> | |
| </div> | |
| <button id="refresh-btn" class="btn"><i class="fas fa-sync-alt"></i> Refresh</button> | |
| </div> | |
| <div class="version-info" id="version-info"> | |
| <!-- Will be populated by JavaScript --> | |
| </div> | |
| <div class="release-notes" id="release-notes"> | |
| <h3><i class="fas fa-file-alt"></i> Latest Release Notes</h3> | |
| <p>Loading release information...</p> | |
| </div> | |
| </div> | |
| <footer> | |
| <p>Data fetched from Odoo's official GitHub repository</p> | |
| </footer> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', function() { | |
| const versionDisplay = document.getElementById('version-display'); | |
| const versionInfo = document.getElementById('version-info'); | |
| const releaseNotes = document.getElementById('release-notes'); | |
| const refreshBtn = document.getElementById('refresh-btn'); | |
| // Fetch latest Odoo version from GitHub | |
| async function fetchLatestVersion() { | |
| try { | |
| versionDisplay.innerHTML = '<div class="loading"></div><p>Fetching latest version...</p>'; | |
| // Fetch tags from Odoo GitHub repository | |
| const response = await fetch('https://api.github.com/repos/odoo/odoo/tags'); | |
| const tags = await response.json(); | |
| if (tags.length === 0) { | |
| throw new Error('No tags found'); | |
| } | |
| // Filter out non-version tags and find the latest version | |
| const versionTags = tags | |
| .map(tag => tag.name) | |
| .filter(name => /^\d+\.\d+$/.test(name)) | |
| .sort((a, b) => { | |
| const aParts = a.split('.').map(Number); | |
| const bParts = b.split('.').map(Number); | |
| return bParts[0] - aParts[0] || bParts[1] - aParts[1]; | |
| }); | |
| if (versionTags.length === 0) { | |
| throw new Error('No valid version tags found'); | |
| } | |
| const latestVersion = versionTags[0]; | |
| const latestTag = tags.find(tag => tag.name === latestVersion); | |
| // Display version | |
| versionDisplay.innerHTML = ` | |
| <div class="version-number">${latestVersion}</div> | |
| <p>Released on: ${new Date(latestTag.commit.author.date).toLocaleDateString()}</p> | |
| `; | |
| // Display additional info | |
| versionInfo.innerHTML = ` | |
| <div class="info-item"> | |
| <i class="fas fa-tag"></i> | |
| <div>Latest Tag</div> | |
| <div>${latestTag.name}</div> | |
| </div> | |
| <div class="info-item"> | |
| <i class="fas fa-calendar"></i> | |
| <div>Published At</div> | |
| <div>${new Date(latestTag.commit.author.date).toLocaleString()}</div> | |
| </div> | |
| <div class="info-item"> | |
| <i class="fas fa-code-branch"></i> | |
| <div>Commit SHA</div> | |
| <div>${latestTag.commit.sha.substring(0, 7)}</div> | |
| </div> | |
| `; | |
| // Fetch release notes (using GitHub releases API) | |
| try { | |
| const releasesResponse = await fetch('https://api.github.com/repos/odoo/odoo/releases'); | |
| const releases = await releasesResponse.json(); | |
| const latestRelease = releases.find(release => | |
| release.tag_name === latestVersion || | |
| release.name.includes(latestVersion) | |
| ); | |
| if (latestRelease) { | |
| const notes = latestRelease.body.split('\n').filter(line => line.trim() !== ''); | |
| releaseNotes.innerHTML = ` | |
| <h3><i class="fas fa-file-alt"></i> Release Notes for v${latestVersion}</h3> | |
| <ul> | |
| ${notes.slice(0, 5).map(note => `<li>${note}</li>`).join('')} | |
| </ul> | |
| <a href="${latestRelease.html_url}" class="btn" target="_blank"> | |
| <i class="fas fa-external-link-alt"></i> View Full Release | |
| </a> | |
| `; | |
| } else { | |
| releaseNotes.innerHTML = ` | |
| <h3><i class="fas fa-file-alt"></i> Release Notes</h3> | |
| <p>No specific release notes found for this version.</p> | |
| `; | |
| } | |
| } catch (error) { | |
| releaseNotes.innerHTML = ` | |
| <h3><i class="fas fa-file-alt"></i> Release Notes</h3> | |
| <p class="error">Could not fetch release notes: ${error.message}</p> | |
| `; | |
| } | |
| } catch (error) { | |
| versionDisplay.innerHTML = ` | |
| <p class="error">Error fetching version: ${error.message}</p> | |
| <p>Please try again later.</p> | |
| `; | |
| console.error('Error:', error); | |
| } | |
| } | |
| // Initial fetch | |
| fetchLatestVersion(); | |
| // Refresh button | |
| refreshBtn.addEventListener('click', fetchLatestVersion); | |
| }); | |
| </script> | |
| </body> | |
| </html> |