@liascript/exporter
Version:
A generic exporter for LiaScript
252 lines (223 loc) • 8.98 kB
HTML
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Export Status - LiaScript Exporter</title>
<link rel="stylesheet" href="/styles.css" />
<script src="/i18n.js"></script>
</head>
<body>
<nav class="navbar">
<div class="navbar-container">
<a href="/" class="navbar-brand">
<img src="./assets/icon.svg" alt="LiaScript" />
</a>
<ul class="navbar-menu">
<li><a href="/" class="navbar-link" data-i18n="nav.home">Home</a></li>
<li><a href="/status.html" class="navbar-link active" data-i18n="nav.status">Status</a></li>
<li class="language-selector-container">
<select id="language-selector" class="language-selector">
<option value="en">EN</option>
<option value="de">DE</option>
</select>
</li>
</ul>
</div>
</nav>
<div class="container">
<header>
<h1 data-i18n="status.title">Export Status</h1>
<p class="subtitle" data-i18n="status.subtitle">Track the status of your export job</p>
</header>
<main>
<section class="card">
<div id="statusContent">
<p data-i18n="status.loading">Loading status...</p>
</div>
<div class="refresh-info" id="refreshInfo">
<span data-i18n="status.autoRefresh">Page will refresh automatically</span>
</div>
</section>
<div style="text-align: center; margin-top: 2rem">
<a href="/" class="btn-secondary" data-i18n="status.backHome">Back to Home</a>
</div>
</main>
<footer>
<p data-i18n="footer.copyright">© 2026 LiaScript Exporter</p>
</footer>
</div>
<script>
const urlParams = new URLSearchParams(window.location.search)
let jobId = urlParams.get('jobId')
let refreshInterval
// Wait for i18n to be ready before loading status
async function initializeStatus() {
if (!window.i18n || !window.i18n.translations[window.i18n.currentLanguage]) {
await new Promise(resolve => {
const checkI18n = setInterval(() => {
if (window.i18n && window.i18n.translations[window.i18n.currentLanguage]) {
clearInterval(checkI18n)
resolve()
}
}, 50)
})
}
if (jobId) {
loadStatus()
refreshInterval = setInterval(loadStatus, 3000)
} else {
// Try to get the last job ID from localStorage
const lastJobId = localStorage.getItem('lastJobId')
if (lastJobId) {
jobId = lastJobId
window.history.replaceState({}, '', `/status.html?jobId=${jobId}`)
loadStatus()
refreshInterval = setInterval(loadStatus, 3000)
} else {
// No job found
document.getElementById('statusContent').innerHTML = `
<div style="text-align: center; padding: 3rem 1rem;">
<p style="color: var(--text-muted); margin-bottom: 1rem;" data-i18n="status.noExport">No export found</p>
<p style="color: var(--text-muted); font-size: 0.9rem;" data-i18n="status.noExportMessage">Start a new export on the home page.</p>
</div>
`
// Re-translate the new content
if (window.i18n) {
window.i18n.updatePageTranslations()
}
document.getElementById('refreshInfo').style.display = 'none'
}
}
}
initializeStatus()
async function loadStatus() {
try {
const response = await fetch(`/api/job/${jobId}`)
if (!response.ok) {
throw new Error(window.i18n ? window.i18n.t('status.error') : 'Job not found')
}
const data = await response.json()
window.lastJobData = data
displayStatus(data)
// Stop refreshing if job is completed or failed
if (data.job.status === 'completed' || data.job.status === 'failed') {
clearInterval(refreshInterval)
}
} catch (error) {
const errorMsg = window.i18n ? window.i18n.t('status.errorMessage') : 'Error loading status'
document.getElementById('statusContent').innerHTML = `
<p class="error-message">${errorMsg}: ${error.message}</p>
`
clearInterval(refreshInterval)
}
}
// Listen for language changes and refresh display (for the card content)
if (window.i18n) {
const originalSetLanguage = window.i18n.setLanguage.bind(window.i18n)
window.i18n.setLanguage = async function(lang) {
await originalSetLanguage(lang)
if (jobId && window.lastJobData) {
displayStatus(window.lastJobData)
}
}
}
function displayStatus(data) {
const { job, queuePosition } = data
const statusClass = `status-${job.status}`
// Get translations
const t = (key, fallback) => window.i18n ? window.i18n.t(key, fallback) : fallback
let statusText = {
queued: t('status.statusQueued', 'Queued'),
processing: t('status.statusProcessing', 'Processing'),
completed: t('status.statusCompleted', 'Completed'),
failed: t('status.statusFailed', 'Failed'),
}[job.status]
let progressPercent = 0
if (job.status === 'processing') progressPercent = 50
if (job.status === 'completed') progressPercent = 100
const sourceText = job.source.type === 'upload'
? t('status.sourceUpload', 'File Upload')
: t('status.sourceGit', 'Git Repository')
let content = `
<h2>${t('status.jobId', 'Job ID')}: ${job.id}</h2>
<div style="margin: 1.5rem 0;">
<span class="status-badge ${statusClass}">${statusText}</span>
</div>
${
queuePosition > 0
? `
<p><strong>${t('status.queuePosition', 'Position in queue')}:</strong> ${queuePosition}</p>
`
: ''
}
<div class="progress-bar">
<div class="progress-fill" style="width: ${progressPercent}%"></div>
</div>
<div style="margin-top: 1.5rem;">
<p><strong>${t('status.source', 'Source')}:</strong> ${sourceText}</p>
${
job.source.gitUrl
? `<p><strong>${t('status.repository', 'Repository')}:</strong> ${job.source.gitUrl}</p>`
: ''
}
${
job.source.files
? `<p><strong>${t('status.files', 'Files')}:</strong> ${job.source.files.length} ${t('status.filesCount', 'file(s)')}</p>`
: ''
}
<p><strong>${t('status.format', 'Format')}:</strong> ${
job.target.preset || job.target.format
}</p>
<p><strong>${t('status.createdAt', 'Created')}:</strong> ${new Date(
job.createdAt
).toLocaleString()}</p>
${
job.startedAt
? `<p><strong>${t('status.startedAt', 'Started')}:</strong> ${new Date(
job.startedAt
).toLocaleString()}</p>`
: ''
}
${
job.completedAt
? `<p><strong>${t('status.completedAt', 'Completed')}:</strong> ${new Date(
job.completedAt
).toLocaleString()}</p>`
: ''
}
</div>
${
job.status === 'failed' && job.error
? `
<div style="margin-top: 1.5rem; padding: 1rem; background: #fee2e2; border-radius: 6px;">
<strong>${t('status.error', 'Error')}:</strong> ${job.error}
</div>
`
: ''
}
${
job.status === 'completed'
? `
<div style="margin-top: 1.5rem;">
<button class="btn-primary" onclick="downloadResult()">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
<polyline points="7 10 12 15 17 10"></polyline>
<line x1="12" y1="15" x2="12" y2="3"></line>
</svg>
${t('status.downloadButton', 'Download File')}
</button>
</div>
`
: ''
}
`
document.getElementById('statusContent').innerHTML = content
}
function downloadResult() {
window.location.href = `/api/download/${jobId}`
}
</script>
</body>
</html>