BLUE
PHP 7.4.33
Path:
/var/www/receipt-app-backend-bitkit.dk/httpdocs/public
Run
Logout
Edit File
Size: 10.39 KB
Close
/var/www/receipt-app-backend-bitkit.dk/httpdocs/public/csv-upload.html
Text
Base64
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>CSV Upload Portal</title> <style> body { font-family: 'Segoe UI', Arial, sans-serif; background: #f5f7fa; margin: 0; padding: 0; } .container { max-width: 500px; margin: 80px auto; background: #fff; border-radius: 20px; box-shadow: 0 4px 32px rgba(0,0,0,0.08); padding: 40px 32px 32px 32px; text-align: center; } .dropzone { border: 2px dashed #b0c4de; border-radius: 14px; padding: 36px 0 28px 0; background: #f8fafc; color: #222; cursor: pointer; margin-bottom: 24px; transition: background 0.2s, border-color 0.2s; position: relative; } .dropzone.dragover { background: #e3eefd; border-color: #1976d2; } .cloud-icon { display: block; margin: 0 auto 14px auto; width: 54px; height: 54px; color: #90a4ae; } .dropzone-text { font-size: 1.13em; color: #444; margin-bottom: 0; } .file-info { margin: 10px 0 18px 0; color: #555; font-size: 1em; } .btn { background: #1976d2; color: #fff; border: none; border-radius: 8px; padding: 14px 0; font-size: 1.13em; font-weight: 600; cursor: pointer; width: 100%; margin-top: 8px; margin-bottom: 10px; transition: background 0.2s; box-shadow: 0 2px 8px rgba(25,118,210,0.08); position: relative; } .btn:disabled { background: #b0c4de; cursor: not-allowed; } .spinner { display: inline-block; width: 20px; height: 20px; border: 3px solid #fff; border-top: 3px solid #1976d2; border-radius: 50%; animation: spin 1s linear infinite; vertical-align: middle; margin-right: 10px; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .progress { margin: 18px 0; height: 4px; background: #e3eefd; border-radius: 2px; overflow: hidden; } .progress-bar { height: 100%; background: #1976d2; width: 0; transition: width 0.3s; } .alert { margin-top: 18px; padding: 14px; border-radius: 6px; font-size: 1em; } .alert-success { background: #e6f4ea; color: #256029; border: 1px solid #b7e1cd; } .alert-error { background: #fdecea; color: #b71c1c; border: 1px solid #f5c6cb; } .note { color: #7b8a99; font-size: 1em; margin-top: 10px; margin-bottom: 0; } ul { text-align: left; margin: 8px 0 0 0; padding-left: 18px; font-size: 0.97em; } .results-section { max-width: 1500px; margin: 40px auto 0 auto; background: #fff; border-radius: 18px; box-shadow: 0 4px 32px rgba(0,0,0,0.08); padding: 32px 28px 28px 28px; text-align: left; min-height: 60px; } @media (max-width: 1200px) { .results-section { max-width: 98vw; } } .results-table { width: 100%; border-collapse: collapse; margin-top: 8px; background: #fff; font-size: 0.98em; box-shadow: 0 1px 8px rgba(0,0,0,0.04); table-layout: auto; } .results-table th, .results-table td { border: 1px solid #e3eefd; padding: 8px 10px; text-align: left; max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .results-table th { background: #f0f4fa; font-weight: 600; color: #1976d2; } .results-table tr:nth-child(even) { background: #f8fafc; } .error-box { background: #fdecea; border: 1px solid #f5c6cb; color: #b71c1c; border-radius: 6px; margin-top: 18px; padding: 12px 16px; font-size: 0.98em; text-align: left; max-width: 100%; overflow-x: auto; } </style> </head> <body> <div class="container"> <h2 style="font-size:2em; font-weight:600; margin-bottom:28px;">CSV Upload</h2> <div id="dropzone" class="dropzone"> <svg class="cloud-icon" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 48 48"><path d="M36 34H14a8 8 0 1 1 2.2-15.7A12 12 0 0 1 44 26a8 8 0 0 1-8 8z"/><path d="M24 20v10m0 0l-4-4m4 4l4-4"/></svg> <div class="dropzone-text">Drag and drop your file here</div> <input id="fileInput" type="file" accept=".csv" style="display:none" /> </div> <div id="fileInfo" class="file-info"></div> <button id="uploadBtn" class="btn" disabled>Upload</button> <div class="progress" id="progress" style="display:none"> <div class="progress-bar" id="progressBar"></div> </div> <div id="alert"></div> <div class="note">Only CSV files are supported</div> </div> <div id="results-section" class="results-section" style="display:none;"> <div id="results"></div> </div> <script> const API_URL = "/notification-entries/upload-csv"; const dropzone = document.getElementById('dropzone'); const fileInput = document.getElementById('fileInput'); const fileInfo = document.getElementById('fileInfo'); const uploadBtn = document.getElementById('uploadBtn'); const alertBox = document.getElementById('alert'); const progress = document.getElementById('progress'); const progressBar = document.getElementById('progressBar'); let selectedFile = null; dropzone.addEventListener('click', () => fileInput.click()); dropzone.addEventListener('dragover', e => { e.preventDefault(); dropzone.classList.add('dragover'); }); dropzone.addEventListener('dragleave', () => dropzone.classList.remove('dragover')); dropzone.addEventListener('drop', e => { e.preventDefault(); dropzone.classList.remove('dragover'); if (e.dataTransfer.files && e.dataTransfer.files[0]) { setFile(e.dataTransfer.files[0]); } }); fileInput.addEventListener('change', e => { if (e.target.files && e.target.files[0]) { setFile(e.target.files[0]); } }); uploadBtn.addEventListener('click', uploadFile); function setFile(file) { selectedFile = file; fileInfo.textContent = `Selected: ${file.name} (${(file.size/1024).toFixed(1)} KB)`; uploadBtn.disabled = false; uploadBtn.textContent = 'Upload'; alertBox.innerHTML = ''; } function showAlert(msg, type = 'success', errors = []) { alertBox.innerHTML = `<div class="alert alert-${type}">${msg} ${errors.length ? `<ul>${errors.slice(0,5).map(e => `<li>${e.error}</li>`).join('')}${errors.length>5?'<li>...and more</li>':''}</ul>` : ''} </div>`; } function renderResultsTable(rows = [], errors = []) { const resultsSection = document.getElementById('results-section'); const resultsDiv = document.getElementById('results'); if ((!rows || !rows.length) && (!errors || !errors.length)) { resultsDiv.innerHTML = ''; resultsSection.style.display = 'none'; return; } let html = ''; if (rows && rows.length) { html += `<div style="overflow-x:auto;"><table class="results-table"><thead><tr>`; const columns = ["id","userId","message","parsedDate","amount","merchant","category","status","createdAt"]; html += columns.map(col => `<th>${col}</th>`).join(''); html += `</tr></thead><tbody>`; html += rows.map(row => `<tr>${columns.map(col => `<td>${col==="amount"?row[col].toLocaleString(undefined,{maximumFractionDigits:2}):col==="parsedDate"||col==="createdAt"?new Date(row[col]).toLocaleString():row[col]}</td>`).join('')}</tr>`).join(''); html += `</tbody></table></div>`; } if (errors && errors.length) { html += `<div class="error-box"><b>Errors:</b><ul>`; html += errors.map(e => `<li><pre style='white-space:pre-wrap;word-break:break-all;'>${JSON.stringify(e.row)}</pre><div style='color:#b71c1c;'>${e.error}</div></li>`).join(''); html += `</ul></div>`; } resultsDiv.innerHTML = html; resultsSection.style.display = ''; } function uploadFile() { if (!selectedFile) return; uploadBtn.disabled = true; uploadBtn.innerHTML = '<span class="spinner"></span>Uploading...'; progress.style.display = 'block'; progressBar.style.width = '0%'; alertBox.innerHTML = ''; const formData = new FormData(); formData.append('file', selectedFile); const xhr = new XMLHttpRequest(); xhr.open('POST', API_URL, true); xhr.upload.onprogress = function(e) { if (e.lengthComputable) { const percent = (e.loaded / e.total) * 100; progressBar.style.width = percent + '%'; } }; xhr.onload = function() { progress.style.display = 'none'; uploadBtn.disabled = false; if (xhr.status === 200) { try { const res = JSON.parse(xhr.responseText); showAlert(`Uploaded: ${res.inserted} rows`, 'success', res.errors || []); renderResultsTable(res.rows, res.errors); // Reset file selection and UI selectedFile = null; fileInput.value = ''; fileInfo.textContent = ''; uploadBtn.disabled = true; uploadBtn.textContent = 'Upload'; // Optionally scroll to results setTimeout(()=>{ const resultsSection = document.getElementById('results-section'); if(resultsSection) resultsSection.scrollIntoView({behavior:'smooth'}); }, 200); } catch { showAlert('Upload succeeded, but could not parse server response.', 'error'); uploadBtn.textContent = 'Upload'; } } else { let msg = 'Failed to upload CSV.'; try { const res = JSON.parse(xhr.responseText); msg = res.error || msg; } catch {} showAlert(msg, 'error'); uploadBtn.textContent = 'Upload'; } }; xhr.onerror = function() { progress.style.display = 'none'; uploadBtn.disabled = false; uploadBtn.textContent = 'Upload'; showAlert('Network error. Please try again.', 'error'); }; xhr.send(formData); } </script> </body> </html>
Save
Close
Exit & Reset
Text mode: syntax highlighting auto-detects file type.
Directory Contents
Dirs: 0 × Files: 1
Delete Selected
Select All
Select None
Sort:
Name
Size
Modified
Enable drag-to-move
Name
Size
Perms
Modified
Actions
csv-upload.html
10.39 KB
lrw-r--r--
2026-04-17 06:08:59
Edit
Download
Rename
Chmod
Change Date
Delete
OK
Cancel
recursive
OK
Cancel
recursive
OK
Cancel
Zip Selected
If ZipArchive is unavailable, a
.tar
will be created (no compression).