382 lines
17 KiB
HTML
382 lines
17 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>PLC Generation Web Application</title>
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
|
<style>
|
|
.upload-area {
|
|
border: 2px dashed #007bff;
|
|
border-radius: 10px;
|
|
padding: 40px;
|
|
text-align: center;
|
|
background-color: #f8f9fa;
|
|
transition: all 0.3s ease;
|
|
}
|
|
.upload-area:hover {
|
|
border-color: #0056b3;
|
|
background-color: #e3f2fd;
|
|
}
|
|
.upload-area.dragover {
|
|
border-color: #28a745;
|
|
background-color: #d4edda;
|
|
}
|
|
.header-icon {
|
|
font-size: 3rem;
|
|
color: #007bff;
|
|
margin-bottom: 20px;
|
|
}
|
|
.btn-primary {
|
|
background-color: #007bff;
|
|
border-color: #007bff;
|
|
padding: 12px 30px;
|
|
font-size: 1.1rem;
|
|
}
|
|
.btn-primary:hover {
|
|
background-color: #0056b3;
|
|
border-color: #0056b3;
|
|
}
|
|
.btn-success {
|
|
padding: 12px 30px;
|
|
font-size: 1.1rem;
|
|
}
|
|
.alert {
|
|
display: none;
|
|
}
|
|
.file-info {
|
|
background-color: #e9ecef;
|
|
border-radius: 5px;
|
|
padding: 10px;
|
|
margin-top: 10px;
|
|
display: none;
|
|
}
|
|
.project-card {
|
|
border: 2px solid #dee2e6;
|
|
border-radius: 10px;
|
|
padding: 20px;
|
|
margin-bottom: 20px;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
}
|
|
.project-card:hover {
|
|
border-color: #007bff;
|
|
background-color: #f8f9fa;
|
|
}
|
|
.project-card.selected {
|
|
border-color: #28a745;
|
|
background-color: #d4edda;
|
|
}
|
|
.project-icon {
|
|
font-size: 2rem;
|
|
color: #007bff;
|
|
margin-bottom: 10px;
|
|
}
|
|
.tab-content {
|
|
margin-top: 20px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container mt-5">
|
|
<div class="row justify-content-center">
|
|
<div class="col-md-10">
|
|
<div class="text-center mb-5">
|
|
<i class="fas fa-microchip header-icon"></i>
|
|
<h1 class="display-4 mb-3">PLC Generation Web Application</h1>
|
|
<p class="lead text-muted">Select a predefined project or upload your Excel file to generate PLC projects automatically</p>
|
|
</div>
|
|
|
|
<!-- Alert Messages -->
|
|
<div id="alertContainer"></div>
|
|
|
|
<!-- Navigation Tabs -->
|
|
<ul class="nav nav-tabs" id="generationTabs" role="tablist">
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link active" id="predefined-tab" data-bs-toggle="tab" data-bs-target="#predefined" type="button" role="tab">
|
|
<i class="fas fa-project-diagram me-2"></i>Projects
|
|
</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" id="custom-tab" data-bs-toggle="tab" data-bs-target="#custom" type="button" role="tab">
|
|
<i class="fas fa-upload me-2"></i>Custom Upload
|
|
</button>
|
|
</li>
|
|
</ul>
|
|
|
|
<!-- Tab Content -->
|
|
<div class="tab-content" id="generationTabContent">
|
|
<!-- Predefined Projects Tab -->
|
|
<div class="tab-pane fade show active" id="predefined" role="tabpanel">
|
|
<div class="card shadow">
|
|
<div class="card-body">
|
|
<h5 class="card-title">
|
|
<i class="fas fa-list me-2"></i>Select a Predefined Project
|
|
</h5>
|
|
<p class="text-muted">Choose from one of the available projects below:</p>
|
|
|
|
<div class="row" id="projectSelection">
|
|
{% for key, project in predefined_projects.items() %}
|
|
<div class="col-md-6">
|
|
<div class="project-card" data-project-key="{{ key }}">
|
|
<div class="text-center">
|
|
<i class="fas fa-microchip project-icon"></i>
|
|
<h6 class="fw-bold">{{ project.display_name }}</h6>
|
|
<small class="text-muted">{{ project.name }}</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
<div class="d-grid mt-4">
|
|
<button type="button" class="btn btn-success btn-lg" id="generatePredefinedBtn" disabled>
|
|
<i class="fas fa-rocket me-2"></i>Generate Selected Project
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Custom Upload Tab -->
|
|
<div class="tab-pane fade" id="custom" role="tabpanel">
|
|
<div class="card shadow">
|
|
<div class="card-body">
|
|
<form id="uploadForm" enctype="multipart/form-data">
|
|
<div class="mb-4">
|
|
<label for="projectName" class="form-label">
|
|
<i class="fas fa-project-diagram me-2"></i>Project Name
|
|
</label>
|
|
<input type="text" class="form-control form-control-lg" id="projectName" name="project_name"
|
|
placeholder="Enter project name (e.g., MTN6_MCM01_UL1_UL3)" required>
|
|
<div class="form-text">Enter a unique name for your PLC project</div>
|
|
</div>
|
|
|
|
<div class="mb-4">
|
|
<label for="excelFile" class="form-label">
|
|
<i class="fas fa-file-excel me-2"></i>Excel File
|
|
</label>
|
|
<div class="upload-area" id="uploadArea">
|
|
<i class="fas fa-cloud-upload-alt fa-3x text-primary mb-3"></i>
|
|
<h5>Drag & Drop your Excel file here</h5>
|
|
<p class="text-muted">or click to browse files</p>
|
|
<input type="file" class="form-control" id="excelFile" name="excel_file"
|
|
accept=".xlsx,.xlsm" required style="display: none;">
|
|
</div>
|
|
<div class="file-info" id="fileInfo">
|
|
<i class="fas fa-file-excel text-success me-2"></i>
|
|
<span id="fileName"></span>
|
|
<span class="text-muted" id="fileSize"></span>
|
|
</div>
|
|
<div class="form-text">Upload .xlsx or .xlsm files only (max 100MB)</div>
|
|
</div>
|
|
|
|
<div class="d-grid">
|
|
<button type="submit" class="btn btn-primary btn-lg" id="submitBtn">
|
|
<i class="fas fa-rocket me-2"></i>Generate PLC Project
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Recent Jobs -->
|
|
<div class="card shadow mt-4" id="recentJobs" style="display: none;">
|
|
<div class="card-header">
|
|
<h5><i class="fas fa-history me-2"></i>Recent Jobs</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div id="jobsList"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
|
|
<script>
|
|
// File upload handling
|
|
const uploadArea = document.getElementById('uploadArea');
|
|
const fileInput = document.getElementById('excelFile');
|
|
const fileInfo = document.getElementById('fileInfo');
|
|
const fileName = document.getElementById('fileName');
|
|
const fileSize = document.getElementById('fileSize');
|
|
const uploadForm = document.getElementById('uploadForm');
|
|
const submitBtn = document.getElementById('submitBtn');
|
|
const alertContainer = document.getElementById('alertContainer');
|
|
|
|
// Project selection handling
|
|
const projectCards = document.querySelectorAll('.project-card');
|
|
const generatePredefinedBtn = document.getElementById('generatePredefinedBtn');
|
|
let selectedProject = null;
|
|
|
|
// Project card selection
|
|
projectCards.forEach(card => {
|
|
card.addEventListener('click', () => {
|
|
// Remove selection from all cards
|
|
projectCards.forEach(c => c.classList.remove('selected'));
|
|
|
|
// Add selection to clicked card
|
|
card.classList.add('selected');
|
|
selectedProject = card.dataset.projectKey;
|
|
|
|
// Enable generate button
|
|
generatePredefinedBtn.disabled = false;
|
|
});
|
|
});
|
|
|
|
// Generate predefined project
|
|
generatePredefinedBtn.addEventListener('click', async () => {
|
|
if (!selectedProject) {
|
|
showAlert('Please select a project first', 'warning');
|
|
return;
|
|
}
|
|
|
|
generatePredefinedBtn.disabled = true;
|
|
generatePredefinedBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Starting Generation...';
|
|
|
|
try {
|
|
const response = await fetch('/generate_predefined', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
project_key: selectedProject
|
|
})
|
|
});
|
|
|
|
const result = await response.json();
|
|
|
|
if (response.ok) {
|
|
showAlert(`Generation started! Redirecting to job status...`, 'success');
|
|
setTimeout(() => {
|
|
window.location.href = `/job/${result.job_id}`;
|
|
}, 2000);
|
|
} else {
|
|
showAlert(`Error: ${result.error}`, 'danger');
|
|
generatePredefinedBtn.disabled = false;
|
|
generatePredefinedBtn.innerHTML = '<i class="fas fa-rocket me-2"></i>Generate Selected Project';
|
|
}
|
|
} catch (error) {
|
|
showAlert(`Network error: ${error.message}`, 'danger');
|
|
generatePredefinedBtn.disabled = false;
|
|
generatePredefinedBtn.innerHTML = '<i class="fas fa-rocket me-2"></i>Generate Selected Project';
|
|
}
|
|
});
|
|
|
|
// Drag and drop handlers for custom upload
|
|
uploadArea.addEventListener('click', () => fileInput.click());
|
|
uploadArea.addEventListener('dragover', (e) => {
|
|
e.preventDefault();
|
|
uploadArea.classList.add('dragover');
|
|
});
|
|
uploadArea.addEventListener('dragleave', () => {
|
|
uploadArea.classList.remove('dragover');
|
|
});
|
|
uploadArea.addEventListener('drop', (e) => {
|
|
e.preventDefault();
|
|
uploadArea.classList.remove('dragover');
|
|
const files = e.dataTransfer.files;
|
|
if (files.length > 0) {
|
|
fileInput.files = files;
|
|
showFileInfo(files[0]);
|
|
}
|
|
});
|
|
|
|
fileInput.addEventListener('change', (e) => {
|
|
if (e.target.files.length > 0) {
|
|
showFileInfo(e.target.files[0]);
|
|
}
|
|
});
|
|
|
|
function showFileInfo(file) {
|
|
fileName.textContent = file.name;
|
|
fileSize.textContent = `(${(file.size / 1024 / 1024).toFixed(2)} MB)`;
|
|
fileInfo.style.display = 'block';
|
|
}
|
|
|
|
function showAlert(message, type = 'info') {
|
|
const alertDiv = document.createElement('div');
|
|
alertDiv.className = `alert alert-${type} alert-dismissible fade show`;
|
|
alertDiv.innerHTML = `
|
|
${message}
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
|
`;
|
|
alertContainer.appendChild(alertDiv);
|
|
}
|
|
|
|
// Form submission for custom upload
|
|
uploadForm.addEventListener('submit', async (e) => {
|
|
e.preventDefault();
|
|
|
|
const formData = new FormData(uploadForm);
|
|
|
|
// Disable submit button
|
|
submitBtn.disabled = true;
|
|
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Uploading...';
|
|
|
|
try {
|
|
const response = await fetch('/upload', {
|
|
method: 'POST',
|
|
body: formData
|
|
});
|
|
|
|
const result = await response.json();
|
|
|
|
if (response.ok) {
|
|
showAlert(`Upload successful! Redirecting to job status...`, 'success');
|
|
setTimeout(() => {
|
|
window.location.href = `/job/${result.job_id}`;
|
|
}, 2000);
|
|
} else {
|
|
showAlert(`Error: ${result.error}`, 'danger');
|
|
submitBtn.disabled = false;
|
|
submitBtn.innerHTML = '<i class="fas fa-rocket me-2"></i>Generate PLC Project';
|
|
}
|
|
} catch (error) {
|
|
showAlert(`Network error: ${error.message}`, 'danger');
|
|
submitBtn.disabled = false;
|
|
submitBtn.innerHTML = '<i class="fas fa-rocket me-2"></i>Generate PLC Project';
|
|
}
|
|
});
|
|
|
|
// Load recent jobs from localStorage
|
|
function loadRecentJobs() {
|
|
const jobs = JSON.parse(localStorage.getItem('plcJobs') || '[]');
|
|
if (jobs.length > 0) {
|
|
document.getElementById('recentJobs').style.display = 'block';
|
|
const jobsList = document.getElementById('jobsList');
|
|
jobsList.innerHTML = jobs.map(job => `
|
|
<div class="d-flex justify-content-between align-items-center p-2 border-bottom">
|
|
<div>
|
|
<strong>${job.name}</strong>
|
|
<small class="text-muted d-block">${job.date}</small>
|
|
</div>
|
|
<a href="/job/${job.id}" class="btn btn-sm btn-outline-primary">View Status</a>
|
|
</div>
|
|
`).join('');
|
|
}
|
|
}
|
|
|
|
// Save job to localStorage
|
|
function saveJob(jobId, projectName) {
|
|
const jobs = JSON.parse(localStorage.getItem('plcJobs') || '[]');
|
|
jobs.unshift({
|
|
id: jobId,
|
|
name: projectName,
|
|
date: new Date().toLocaleString()
|
|
});
|
|
// Keep only last 5 jobs
|
|
jobs.splice(5);
|
|
localStorage.setItem('plcJobs', JSON.stringify(jobs));
|
|
}
|
|
|
|
// Load recent jobs on page load
|
|
loadRecentJobs();
|
|
</script>
|
|
</body>
|
|
</html> |