232 lines
5.8 KiB
JavaScript
232 lines
5.8 KiB
JavaScript
/**
|
|
* Form utilities for handling form submissions with progress tracking
|
|
*/
|
|
|
|
class FormHandler {
|
|
constructor(formId, options = {}) {
|
|
this.form = document.getElementById(formId);
|
|
this.options = {
|
|
progressModalId: "progressModal",
|
|
progressBarId: "progressBar",
|
|
progressStatusId: "progressStatus",
|
|
statusCheckInterval: 1000,
|
|
onSuccess: null,
|
|
onError: null,
|
|
onProgress: null,
|
|
...options,
|
|
};
|
|
|
|
this.progressModal = null;
|
|
this.progressBar = null;
|
|
this.progressStatus = null;
|
|
this.submitButton = null;
|
|
|
|
this.initElements();
|
|
this.initEventListeners();
|
|
}
|
|
|
|
/**
|
|
* Initialize DOM elements
|
|
*/
|
|
initElements() {
|
|
if (this.options.progressModalId) {
|
|
const modalElement = document.getElementById(
|
|
this.options.progressModalId
|
|
);
|
|
if (modalElement && typeof bootstrap !== "undefined") {
|
|
this.progressModal = new bootstrap.Modal(modalElement);
|
|
}
|
|
}
|
|
|
|
this.progressBar = document.getElementById(this.options.progressBarId);
|
|
this.progressStatus = document.getElementById(
|
|
this.options.progressStatusId
|
|
);
|
|
this.submitButton = this.form?.querySelector('button[type="submit"]');
|
|
}
|
|
|
|
/**
|
|
* Initialize event listeners
|
|
*/
|
|
initEventListeners() {
|
|
if (this.form) {
|
|
this.form.addEventListener("submit", (e) => this.handleSubmit(e));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle form submission
|
|
* @param {Event} e - Form submit event
|
|
*/
|
|
async handleSubmit(e) {
|
|
e.preventDefault();
|
|
|
|
// Show progress modal
|
|
this.showProgress();
|
|
this.updateProgress(5, "Starting...");
|
|
|
|
// Disable submit button
|
|
if (this.submitButton) {
|
|
this.submitButton.disabled = true;
|
|
}
|
|
|
|
const formData = new FormData(this.form);
|
|
|
|
try {
|
|
const response = await fetch(this.form.action, {
|
|
method: "POST",
|
|
body: formData,
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.error) {
|
|
this.handleError(data.error);
|
|
return;
|
|
}
|
|
|
|
// Start polling for task status if task_id is provided
|
|
if (data.task_id) {
|
|
this.pollTaskStatus(data.task_id);
|
|
} else {
|
|
// Handle immediate response
|
|
this.handleSuccess(data);
|
|
}
|
|
} catch (error) {
|
|
console.error("Form submission failed:", error);
|
|
this.handleError("Form submission failed. Please try again.");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Poll task status for long-running operations
|
|
* @param {string} taskId - Task ID to poll
|
|
*/
|
|
async pollTaskStatus(taskId) {
|
|
const checkStatus = async () => {
|
|
try {
|
|
// Construct status URL - this should be customizable
|
|
const statusUrl = this.options.statusUrlTemplate
|
|
? this.options.statusUrlTemplate.replace("{taskId}", taskId)
|
|
: `/upload/task_status/${taskId}`;
|
|
|
|
const response = await fetch(statusUrl);
|
|
const status = await response.json();
|
|
|
|
console.log("Task status:", status);
|
|
|
|
if (status.state === "SUCCESS") {
|
|
this.updateProgress(100, "Completed!");
|
|
setTimeout(() => {
|
|
this.hideProgress();
|
|
this.handleSuccess(status.result);
|
|
}, 1000);
|
|
} else if (status.state === "FAILURE") {
|
|
this.updateProgress(100, "Failed!", true);
|
|
setTimeout(() => {
|
|
this.hideProgress();
|
|
this.handleError(status.error || "Unknown error occurred");
|
|
}, 1000);
|
|
} else {
|
|
// Update progress
|
|
const progress = status.progress || 0;
|
|
this.updateProgress(progress, `Processing... (${status.state})`);
|
|
|
|
// Continue polling
|
|
setTimeout(checkStatus, this.options.statusCheckInterval);
|
|
}
|
|
} catch (error) {
|
|
console.error("Failed to check task status:", error);
|
|
// Continue polling on error
|
|
setTimeout(checkStatus, this.options.statusCheckInterval);
|
|
}
|
|
};
|
|
|
|
checkStatus();
|
|
}
|
|
|
|
/**
|
|
* Show progress modal
|
|
*/
|
|
showProgress() {
|
|
if (this.progressModal) {
|
|
this.progressModal.show();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Hide progress modal
|
|
*/
|
|
hideProgress() {
|
|
if (this.progressModal) {
|
|
this.progressModal.hide();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update progress display
|
|
* @param {number} percentage - Progress percentage (0-100)
|
|
* @param {string} message - Status message
|
|
* @param {boolean} isError - Whether this is an error state
|
|
*/
|
|
updateProgress(percentage, message, isError = false) {
|
|
if (this.progressBar) {
|
|
this.progressBar.style.width = `${percentage}%`;
|
|
this.progressBar.textContent = `${percentage}%`;
|
|
|
|
if (isError) {
|
|
this.progressBar.classList.add("bg-danger");
|
|
}
|
|
}
|
|
|
|
if (this.progressStatus) {
|
|
this.progressStatus.textContent = message;
|
|
}
|
|
|
|
// Call custom progress callback
|
|
if (this.options.onProgress) {
|
|
this.options.onProgress(percentage, message, isError);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle successful form submission
|
|
* @param {object} result - Success result data
|
|
*/
|
|
handleSuccess(result) {
|
|
// Re-enable submit button
|
|
if (this.submitButton) {
|
|
this.submitButton.disabled = false;
|
|
}
|
|
|
|
// Call custom success callback
|
|
if (this.options.onSuccess) {
|
|
this.options.onSuccess(result);
|
|
} else {
|
|
// Default success handling
|
|
showFlashMessage("Operation completed successfully!", "success");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle form submission error
|
|
* @param {string} error - Error message
|
|
*/
|
|
handleError(error) {
|
|
this.hideProgress();
|
|
|
|
// Re-enable submit button
|
|
if (this.submitButton) {
|
|
this.submitButton.disabled = false;
|
|
}
|
|
|
|
// Call custom error callback
|
|
if (this.options.onError) {
|
|
this.options.onError(error);
|
|
} else {
|
|
// Default error handling
|
|
showFlashMessage(`Error: ${error}`, "error");
|
|
}
|
|
}
|
|
}
|