Add upload progress bar to recounting upload dashboard

This commit is contained in:
proitlab committed 2026-08-05 00:39:47 +07:00
1 parent bcceaae626
commit 79656cd661
1 file changed
+95 -26
+95 -26
View File
@@ -349,6 +349,32 @@
.upload-status.success { color: var(--accent4); } .upload-status.success { color: var(--accent4); }
.upload-status.error { color: var(--accent3); } .upload-status.error { color: var(--accent3); }
.upload-progress {
display: none;
width: 100%;
height: 6px;
background: var(--cell-bg);
border: 1px solid var(--cell-border);
border-radius: 3px;
margin-top: 10px;
overflow: hidden;
}
.upload-progress .upload-progress-bar {
height: 100%;
width: 0%;
background: linear-gradient(90deg, var(--accent), var(--accent2));
border-radius: 3px;
transition: width 0.15s ease;
}
.upload-progress-percent {
display: none;
font-size: 8px;
color: var(--text-secondary);
letter-spacing: 1px;
text-align: center;
margin-top: 4px;
}
.btn { .btn {
font-family: 'Orbitron', sans-serif; font-size: 10px; letter-spacing: 2px; font-family: 'Orbitron', sans-serif; font-size: 10px; letter-spacing: 2px;
padding: 8px 16px; border-radius: 7px; cursor: pointer; font-weight: 600; padding: 8px 16px; border-radius: 7px; cursor: pointer; font-weight: 600;
@@ -482,6 +508,10 @@
<button class="btn btn-ghost" id="btn-upload" onclick="uploadFile()">UPLOAD</button> <button class="btn btn-ghost" id="btn-upload" onclick="uploadFile()">UPLOAD</button>
</div> </div>
<div class="upload-progress" id="upload-progress">
<div class="upload-progress-bar" id="upload-progress-bar"></div>
</div>
<div class="upload-progress-percent" id="upload-progress-percent"></div>
<div class="upload-status" id="upload-status"></div> <div class="upload-status" id="upload-status"></div>
<input type="file" id="upload-file-input" accept=".mp4" style="display:none" <input type="file" id="upload-file-input" accept=".mp4" style="display:none"
@@ -558,6 +588,14 @@ function setPendingFile(file) {
document.getElementById('pending-filename').classList.add('has-file'); document.getElementById('pending-filename').classList.add('has-file');
document.getElementById('upload-status').textContent = ''; document.getElementById('upload-status').textContent = '';
document.getElementById('upload-status').className = 'upload-status'; document.getElementById('upload-status').className = 'upload-status';
showUploadProgress(false);
}
function showUploadProgress(show) {
document.getElementById('upload-progress').style.display = show ? 'block' : 'none';
document.getElementById('upload-progress-percent').style.display = show ? 'block' : 'none';
document.getElementById('upload-progress-bar').style.width = '0%';
document.getElementById('upload-progress-percent').textContent = '';
} }
function showUploadStatus(msg, cls) { function showUploadStatus(msg, cls) {
@@ -566,7 +604,7 @@ function showUploadStatus(msg, cls) {
el.className = 'upload-status ' + (cls || ''); el.className = 'upload-status ' + (cls || '');
} }
async function uploadFile() { function uploadFile() {
if (!pendingUploadFile) { if (!pendingUploadFile) {
showUploadStatus('Pick a file first', 'error'); showUploadStatus('Pick a file first', 'error');
return; return;
@@ -575,32 +613,63 @@ async function uploadFile() {
btn.disabled = true; btn.disabled = true;
btn.textContent = 'UPLOADING...'; btn.textContent = 'UPLOADING...';
showUploadStatus('Uploading...', ''); showUploadStatus('Uploading...', '');
try { showUploadProgress(true);
var form = new FormData();
form.append('file', pendingUploadFile); var xhr = new XMLHttpRequest();
var res = await fetch('/api/upload', { method: 'POST', body: form }); xhr.open('POST', '/api/upload');
var data = await res.json();
if (!data.success) { xhr.upload.onprogress = function(e) {
showUploadStatus(data.error || 'Upload failed', 'error'); if (e.lengthComputable) {
btn.disabled = false; var pct = Math.round((e.loaded / e.total) * 100);
btn.textContent = 'UPLOAD'; document.getElementById('upload-progress-bar').style.width = pct + '%';
return; document.getElementById('upload-progress-percent').textContent = pct + '%';
} }
showUploadStatus('Upload complete: ' + data.name, 'success'); };
selectedFile = data.path;
document.getElementById('btn-start').disabled = false; xhr.onload = function() {
document.getElementById('btn-download').disabled = false; showUploadProgress(false);
document.getElementById('btn-delete').disabled = false; try {
document.getElementById('pending-filename').textContent = 'no file chosen'; var data = JSON.parse(xhr.responseText);
document.getElementById('pending-filename').classList.remove('has-file'); if (!data.success) {
pendingUploadFile = null; showUploadStatus(data.error || 'Upload failed', 'error');
loadUploads(); btn.disabled = false;
loadBatchResult(data.path); btn.textContent = 'UPLOAD';
} catch (err) { return;
showUploadStatus('Upload error: ' + err.message, 'error'); }
} showUploadStatus('Upload complete: ' + data.name, 'success');
btn.disabled = false; selectedFile = data.path;
btn.textContent = 'UPLOAD'; document.getElementById('btn-start').disabled = false;
document.getElementById('btn-download').disabled = false;
document.getElementById('btn-delete').disabled = false;
document.getElementById('pending-filename').textContent = 'no file chosen';
document.getElementById('pending-filename').classList.remove('has-file');
pendingUploadFile = null;
loadUploads();
loadBatchResult(data.path);
} catch (err) {
showUploadStatus('Upload error: ' + err.message, 'error');
}
btn.disabled = false;
btn.textContent = 'UPLOAD';
};
xhr.onerror = function() {
showUploadProgress(false);
showUploadStatus('Upload error: network error', 'error');
btn.disabled = false;
btn.textContent = 'UPLOAD';
};
xhr.onabort = function() {
showUploadProgress(false);
showUploadStatus('Upload aborted', '');
btn.disabled = false;
btn.textContent = 'UPLOAD';
};
var form = new FormData();
form.append('file', pendingUploadFile);
xhr.send(form);
} }
function selectFile(path, name) { function selectFile(path, name) {