bmc_hub/app/customers/frontend/bmc_office_upload.html

289 lines
11 KiB
HTML
Raw Normal View History

<!DOCTYPE html>
<html lang="da">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BMC Office Abonnementer - Upload</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css">
<style>
:root {
--primary: #0f4c75;
--accent: #3282b8;
}
body {
background: #f8f9fa;
}
.upload-zone {
border: 3px dashed var(--accent);
border-radius: 15px;
padding: 60px 20px;
text-align: center;
cursor: pointer;
transition: all 0.3s;
background: white;
}
.upload-zone:hover {
border-color: var(--primary);
background: #f8f9fa;
}
.upload-zone.dragover {
background: #e3f2fd;
border-color: var(--primary);
transform: scale(1.02);
}
.stats-card {
background: white;
border-radius: 10px;
padding: 20px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.stat-item {
text-align: center;
padding: 15px;
}
.stat-number {
font-size: 2.5rem;
font-weight: bold;
color: var(--primary);
}
.stat-label {
color: #6c757d;
font-size: 0.9rem;
margin-top: 5px;
}
.progress-container {
display: none;
margin-top: 20px;
}
.error-list {
max-height: 300px;
overflow-y: auto;
}
</style>
</head>
<body>
<div class="container py-5">
<div class="row">
<div class="col-lg-8 mx-auto">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2 class="fw-bold">
<i class="bi bi-cloud-upload text-primary me-2"></i>
BMC Office Abonnementer
</h2>
<a href="/customers" class="btn btn-outline-secondary">
<i class="bi bi-arrow-left me-2"></i>Tilbage
</a>
</div>
<!-- Info Card -->
<div class="alert alert-info">
<h5 class="alert-heading">
<i class="bi bi-info-circle me-2"></i>Om Upload
</h5>
<p class="mb-0">Upload en Excel fil (.xlsx eller .xls) med BMC Office abonnementsdata.
Systemet vil automatisk matche firma navne med kunder i databasen.</p>
<hr>
<small class="text-muted">
<strong>Forventede kolonner:</strong> FirmaID, Firma, Startdate, Text, Antal, Pris, Rabat, Beskrivelse, FakturaFirmaID, FakturaFirma
</small>
</div>
<!-- Upload Zone -->
<div class="upload-zone" id="uploadZone">
<i class="bi bi-cloud-arrow-up" style="font-size: 4rem; color: var(--accent);"></i>
<h4 class="mt-3">Træk Excel fil hertil</h4>
<p class="text-muted">eller klik for at vælge fil</p>
<input type="file" id="fileInput" accept=".xlsx,.xls" style="display: none;">
<button class="btn btn-primary mt-3" onclick="document.getElementById('fileInput').click()">
<i class="bi bi-folder2-open me-2"></i>Vælg Fil
</button>
</div>
<!-- Progress -->
<div class="progress-container" id="progressContainer">
<div class="progress" style="height: 30px;">
<div class="progress-bar progress-bar-striped progress-bar-animated"
role="progressbar"
id="progressBar"
style="width: 0%">
<span id="progressText">Uploader...</span>
</div>
</div>
</div>
<!-- Results -->
<div id="resultsContainer" class="mt-4" style="display: none;">
<!-- Statistics -->
<div class="stats-card">
<h5 class="fw-bold mb-4">
<i class="bi bi-bar-chart text-success me-2"></i>Import Resultat
</h5>
<div class="row text-center">
<div class="col-md-3">
<div class="stat-item">
<div class="stat-number text-primary" id="statImported">0</div>
<div class="stat-label">Importeret</div>
</div>
</div>
<div class="col-md-3">
<div class="stat-item">
<div class="stat-number text-warning" id="statSkipped">0</div>
<div class="stat-label">Sprunget Over</div>
</div>
</div>
<div class="col-md-3">
<div class="stat-item">
<div class="stat-number text-success" id="statActive">0</div>
<div class="stat-label">Aktive</div>
</div>
</div>
<div class="col-md-3">
<div class="stat-item">
<div class="stat-number text-info" id="statValue">0</div>
<div class="stat-label">DKK (Total)</div>
</div>
</div>
</div>
</div>
<!-- Errors (if any) -->
<div id="errorsCard" class="stats-card" style="display: none;">
<h5 class="fw-bold text-danger mb-3">
<i class="bi bi-exclamation-triangle me-2"></i>Advarsler
</h5>
<div class="error-list" id="errorList"></div>
</div>
<!-- Actions -->
<div class="text-center mt-4">
<button class="btn btn-primary btn-lg" onclick="location.reload()">
<i class="bi bi-arrow-clockwise me-2"></i>Upload Ny Fil
</button>
<a href="/customers" class="btn btn-outline-secondary btn-lg">
<i class="bi bi-people me-2"></i>Gå til Kunder
</a>
</div>
</div>
</div>
</div>
</div>
<script>
const uploadZone = document.getElementById('uploadZone');
const fileInput = document.getElementById('fileInput');
const progressContainer = document.getElementById('progressContainer');
const progressBar = document.getElementById('progressBar');
const progressText = document.getElementById('progressText');
const resultsContainer = document.getElementById('resultsContainer');
// Drag & Drop
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
uploadZone.addEventListener(eventName, preventDefaults, false);
});
function preventDefaults(e) {
e.preventDefault();
e.stopPropagation();
}
['dragenter', 'dragover'].forEach(eventName => {
uploadZone.addEventListener(eventName, () => {
uploadZone.classList.add('dragover');
});
});
['dragleave', 'drop'].forEach(eventName => {
uploadZone.addEventListener(eventName, () => {
uploadZone.classList.remove('dragover');
});
});
uploadZone.addEventListener('drop', handleDrop);
fileInput.addEventListener('change', handleFileSelect);
function handleDrop(e) {
const dt = e.dataTransfer;
const files = dt.files;
if (files.length > 0) {
uploadFile(files[0]);
}
}
function handleFileSelect(e) {
const files = e.target.files;
if (files.length > 0) {
uploadFile(files[0]);
}
}
async function uploadFile(file) {
if (!file.name.endsWith('.xlsx') && !file.name.endsWith('.xls')) {
alert('Kun Excel filer (.xlsx, .xls) er tilladt');
return;
}
uploadZone.style.display = 'none';
progressContainer.style.display = 'block';
progressBar.style.width = '50%';
progressText.textContent = 'Uploader og importerer...';
const formData = new FormData();
formData.append('file', file);
try {
const response = await fetch('/api/v1/admin/bmc-office-subscriptions/upload', {
method: 'POST',
body: formData
});
progressBar.style.width = '100%';
progressText.textContent = 'Færdig!';
const result = await response.json();
if (!response.ok) {
throw new Error(result.detail || 'Upload fejlede');
}
displayResults(result);
} catch (error) {
console.error('Upload error:', error);
alert('Fejl ved upload: ' + error.message);
location.reload();
}
}
function displayResults(result) {
progressContainer.style.display = 'none';
resultsContainer.style.display = 'block';
document.getElementById('statImported').textContent = result.imported;
document.getElementById('statSkipped').textContent = result.skipped;
document.getElementById('statActive').textContent = result.statistics.active_records;
document.getElementById('statValue').textContent = result.statistics.total_value_dkk.toFixed(2);
if (result.errors && result.errors.length > 0) {
document.getElementById('errorsCard').style.display = 'block';
const errorList = document.getElementById('errorList');
errorList.innerHTML = result.errors.map(err =>
`<div class="alert alert-warning py-2 mb-2"><small>${err}</small></div>`
).join('');
}
}
</script>
</body>
</html>