Files
osm-import-tools/web/templates/index.html
2025-12-05 14:29:17 -08:00

312 lines
9.1 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>The Villages Import Tools</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
background: #f5f5f5;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: white;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h1 {
color: #333;
margin-bottom: 10px;
}
.subtitle {
color: #666;
margin-bottom: 30px;
}
.section {
margin-bottom: 30px;
}
.section h2 {
color: #444;
margin-bottom: 15px;
font-size: 20px;
}
.button-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 15px;
margin-bottom: 20px;
}
.script-button {
padding: 15px 20px;
background: #007bff;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: background 0.2s;
}
.script-button:hover {
background: #0056b3;
}
.script-button:disabled {
background: #ccc;
cursor: not-allowed;
}
.script-button.lake {
background: #28a745;
}
.script-button.lake:hover {
background: #218838;
}
.script-button.sumter {
background: #17a2b8;
}
.script-button.sumter:hover {
background: #138496;
}
.map-link {
display: inline-block;
padding: 15px 30px;
background: #6f42c1;
color: white;
text-decoration: none;
border-radius: 6px;
font-weight: 500;
transition: background 0.2s;
}
.map-link:hover {
background: #5a32a3;
}
.log-viewer {
margin-top: 30px;
border-top: 2px solid #eee;
padding-top: 20px;
}
.log-box {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 6px;
font-family: 'Courier New', monospace;
font-size: 12px;
max-height: 400px;
overflow-y: auto;
white-space: pre-wrap;
word-wrap: break-word;
display: none;
}
.log-box.active {
display: block;
}
.status-message {
padding: 10px 15px;
border-radius: 6px;
margin-bottom: 15px;
display: none;
}
.status-message.success {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.status-message.error {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.status-message.info {
background: #d1ecf1;
color: #0c5460;
border: 1px solid #bee5eb;
}
.county-group {
margin-bottom: 25px;
}
.county-group h3 {
color: #555;
margin-bottom: 10px;
font-size: 16px;
}
</style>
</head>
<body>
<div class="container">
<h1>The Villages Import Tools</h1>
<p class="subtitle">Run data processing scripts and view results</p>
<div class="section">
<h2>Map Viewer</h2>
<a href="/map" class="map-link">Open GeoJSON Map Viewer</a>
</div>
<div class="section">
<h2>Data Processing Scripts</h2>
{% for category, script_names in scripts_by_category.items() %}
<div class="county-group">
<h3>{{ category }}</h3>
{% for script_name in script_names %}
{% if script_name in script_map %}
{% set script_config = script_map[script_name] %}
{% if script_config is string %}
{# Simple command with no county selection #}
<div class="button-grid">
<button class="script-button" onclick="runScript('{{ script_name }}', '')">
{{ script_name|replace('-', ' ')|title }}
</button>
</div>
{% elif script_config is mapping %}
{# County-specific commands #}
<div class="button-grid">
{% if 'lake' in script_config %}
<button class="script-button lake" onclick="runScript('{{ script_name }}', 'lake')">
{{ script_name|replace('-', ' ')|title }} (Lake)
</button>
{% endif %}
{% if 'sumter' in script_config %}
<button class="script-button sumter" onclick="runScript('{{ script_name }}', 'sumter')">
{{ script_name|replace('-', ' ')|title }} (Sumter)
</button>
{% endif %}
</div>
{% endif %}
{% endif %}
{% endfor %}
</div>
{% endfor %}
</div>
<div class="log-viewer">
<h2>Script Output</h2>
<div id="status" class="status-message"></div>
<div id="logs" class="log-box"></div>
</div>
</div>
<script>
let currentJobId = null;
let logCheckInterval = null;
function showStatus(message, type) {
const statusEl = document.getElementById('status');
statusEl.textContent = message;
statusEl.className = `status-message ${type}`;
statusEl.style.display = 'block';
}
function runScript(scriptName, county) {
const logsEl = document.getElementById('logs');
logsEl.textContent = 'Starting script...\n';
logsEl.classList.add('active');
showStatus(`Running ${scriptName} for ${county}...`, 'info');
// Disable all buttons
document.querySelectorAll('.script-button').forEach(btn => {
btn.disabled = true;
});
fetch('/api/run-script', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
script: scriptName,
county: county
})
})
.then(response => response.json())
.then(data => {
if (data.error) {
showStatus(`Error: ${data.error}`, 'error');
enableButtons();
return;
}
currentJobId = data.job_id;
showStatus(`Script started (Job ID: ${data.job_id})`, 'success');
// Start polling for logs
if (logCheckInterval) {
clearInterval(logCheckInterval);
}
logCheckInterval = setInterval(checkJobStatus, 1000);
})
.catch(error => {
showStatus(`Error: ${error.message}`, 'error');
enableButtons();
});
}
function checkJobStatus() {
if (!currentJobId) return;
fetch(`/api/job-status/${currentJobId}`)
.then(response => response.json())
.then(data => {
const logsEl = document.getElementById('logs');
logsEl.textContent = data.logs.join('');
// Auto-scroll to bottom
logsEl.scrollTop = logsEl.scrollHeight;
if (!data.running) {
clearInterval(logCheckInterval);
showStatus('Script completed', 'success');
enableButtons();
currentJobId = null;
}
})
.catch(error => {
console.error('Error checking job status:', error);
});
}
function enableButtons() {
document.querySelectorAll('.script-button').forEach(btn => {
btn.disabled = false;
});
}
</script>
</body>
</html>