Boilerplate yield and back and forth
This commit is contained in:
181
static/js/quote_upload.js
Normal file
181
static/js/quote_upload.js
Normal file
@@ -0,0 +1,181 @@
|
||||
const dropArea = document.getElementById('dropArea');
|
||||
const fileInput = document.getElementById('fileInput');
|
||||
const fileList = document.getElementById('fileList');
|
||||
const submitBtn = document.getElementById('submitBtn');
|
||||
const emailInput = document.getElementById('emailInput');
|
||||
const notesInput = document.getElementById('notesInput');
|
||||
let selectedFiles = [];
|
||||
|
||||
function checkCanSubmit() {
|
||||
const hasEmail = emailInput.value.trim().length > 0;
|
||||
const hasNotes = notesInput.value.trim().length > 0;
|
||||
const hasFiles = selectedFiles.length > 0;
|
||||
submitBtn.disabled = !(hasEmail || hasNotes || hasFiles);
|
||||
}
|
||||
|
||||
emailInput.addEventListener('input', checkCanSubmit);
|
||||
notesInput.addEventListener('input', checkCanSubmit);
|
||||
|
||||
dropArea.addEventListener('click', () => fileInput.click());
|
||||
|
||||
dropArea.addEventListener('dragover', (e) => {
|
||||
e.preventDefault();
|
||||
dropArea.classList.add('dragover');
|
||||
});
|
||||
|
||||
dropArea.addEventListener('dragleave', () => {
|
||||
dropArea.classList.remove('dragover');
|
||||
});
|
||||
|
||||
dropArea.addEventListener('drop', (e) => {
|
||||
e.preventDefault();
|
||||
dropArea.classList.remove('dragover');
|
||||
handleFiles(e.dataTransfer.files);
|
||||
});
|
||||
|
||||
fileInput.addEventListener('change', (e) => {
|
||||
handleFiles(e.target.files);
|
||||
});
|
||||
|
||||
function handleFiles(files) {
|
||||
selectedFiles = Array.from(files);
|
||||
displayFiles();
|
||||
checkCanSubmit();
|
||||
}
|
||||
|
||||
function displayFiles() {
|
||||
if (selectedFiles.length === 0) {
|
||||
fileList.classList.remove('show');
|
||||
return;
|
||||
}
|
||||
fileList.classList.add('show');
|
||||
fileList.innerHTML = selectedFiles.map(file =>
|
||||
`<div class="file-item">${file.name} (${(file.size / 1024).toFixed(1)} KB)</div>`
|
||||
).join('');
|
||||
}
|
||||
|
||||
submitBtn.addEventListener('click', async () => {
|
||||
const email = emailInput.value.trim();
|
||||
const notes = notesInput.value.trim();
|
||||
|
||||
// Validate at least one field
|
||||
if (!email && !notes && selectedFiles.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable form during submission
|
||||
submitBtn.disabled = true;
|
||||
emailInput.disabled = true;
|
||||
notesInput.disabled = true;
|
||||
fileInput.disabled = true;
|
||||
dropArea.style.pointerEvents = 'none';
|
||||
dropArea.classList.add('disabled');
|
||||
|
||||
// Show progress bar
|
||||
const progressContainer = document.getElementById('progressContainer');
|
||||
const progressFill = document.getElementById('progressFill');
|
||||
const progressMessage = document.getElementById('progressMessage');
|
||||
progressContainer.style.display = 'block';
|
||||
progressFill.style.width = '0%';
|
||||
progressMessage.textContent = 'Starting...';
|
||||
|
||||
try {
|
||||
// Create FormData
|
||||
const formData = new FormData();
|
||||
if (email) formData.append('email', email);
|
||||
if (notes) formData.append('notes', notes);
|
||||
selectedFiles.forEach(file => {
|
||||
formData.append('files', file);
|
||||
});
|
||||
|
||||
// Submit and read streaming response
|
||||
const response = await fetch('/submit-quote/', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Server error: ${response.status}`);
|
||||
}
|
||||
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
let buffer = '';
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
|
||||
buffer += decoder.decode(value, { stream: true });
|
||||
const lines = buffer.split('\n');
|
||||
buffer = lines.pop() || ''; // Keep incomplete line in buffer
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.trim()) {
|
||||
try {
|
||||
const update = JSON.parse(line);
|
||||
progressFill.style.width = update.progress + '%';
|
||||
progressMessage.textContent = update.message;
|
||||
|
||||
if (update.step === 5 || update.step === 'error') {
|
||||
// Complete or error
|
||||
if (update.step === 5) {
|
||||
setTimeout(() => {
|
||||
// Reset form after success
|
||||
emailInput.value = '';
|
||||
notesInput.value = '';
|
||||
selectedFiles = [];
|
||||
displayFiles();
|
||||
checkCanSubmit();
|
||||
|
||||
// Re-enable form (but keep progress bar visible)
|
||||
emailInput.disabled = false;
|
||||
notesInput.disabled = false;
|
||||
fileInput.disabled = false;
|
||||
dropArea.style.pointerEvents = 'auto';
|
||||
dropArea.classList.remove('disabled');
|
||||
}, 2000);
|
||||
} else {
|
||||
// Error - re-enable form
|
||||
emailInput.disabled = false;
|
||||
notesInput.disabled = false;
|
||||
fileInput.disabled = false;
|
||||
dropArea.style.pointerEvents = 'auto';
|
||||
dropArea.classList.remove('disabled');
|
||||
submitBtn.disabled = false;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error parsing progress update:', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
progressMessage.textContent = `Error: ${error.message}`;
|
||||
progressFill.style.width = '0%';
|
||||
|
||||
// Re-enable form
|
||||
emailInput.disabled = false;
|
||||
notesInput.disabled = false;
|
||||
fileInput.disabled = false;
|
||||
dropArea.style.pointerEvents = 'auto';
|
||||
dropArea.classList.remove('disabled');
|
||||
submitBtn.disabled = false;
|
||||
}
|
||||
});
|
||||
|
||||
// Attempt to auto-resize the iframe height (if parent allows)
|
||||
function postHeight() {
|
||||
const height = document.documentElement.scrollHeight;
|
||||
try {
|
||||
parent.postMessage({ type: 'sedutto-ifr-height', height }, '*');
|
||||
} catch (e) {
|
||||
// ignore if cross-origin restrictions apply
|
||||
}
|
||||
}
|
||||
window.addEventListener('load', postHeight);
|
||||
window.addEventListener('resize', postHeight);
|
||||
const mo = new MutationObserver(postHeight);
|
||||
mo.observe(document.body, { childList: true, subtree: true });
|
||||
|
||||
Reference in New Issue
Block a user