Browse Source

chore: vibe coded some progress indication

document-upload-removal-layout-update
nic 10 months ago
parent
commit
ab674944db
  1. 99
      static/css/upload.css
  2. 146
      templates/partials/document_upload.html

99
static/css/upload.css

@ -151,6 +151,7 @@
color: var(--content-text);
}
/* Original Spinner - used for general loading indicators */
.spinner {
border: 3px solid rgba(0, 0, 0, 0.1);
border-radius: 50%;
@ -160,6 +161,17 @@
animation: spin 1s linear infinite;
}
/* Larger spinner specifically for overlays */
.overlay-spinner {
width: 50px;
height: 50px;
margin: 0 auto;
border: 3px solid rgba(255, 255, 255, 0.3);
border-radius: 50%;
border-top-color: white;
animation: spin 1s ease-in-out infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
@ -252,16 +264,16 @@
/* HTMX indicator styles */
.htmx-indicator {
display: none;
opacity: 0;
transition: opacity 200ms ease-in;
}
.htmx-request .htmx-indicator {
opacity: 1;
}
.htmx-request.htmx-indicator {
opacity: 1;
display: flex;
align-items: center;
justify-content: center;
}
.loading-indicator {
@ -349,4 +361,83 @@
margin: 0.5rem 0;
font-size: 0.9rem;
color: var(--soft-text);
}
/* Upload Overlay */
.upload-container {
position: relative;
min-height: 200px;
width: 100%;
}
.upload-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.95), rgba(240, 240, 240, 0.95));
display: none;
justify-content: center;
align-items: center;
z-index: 1000;
border-radius: inherit;
}
.upload-overlay.htmx-request {
display: flex;
}
.upload-overlay-content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
max-width: 80%;
}
.upload-overlay h3 {
margin-bottom: 0.5rem;
color: #333;
}
.upload-overlay p {
margin-top: 0.5rem;
color: #555;
}
.upload-overlay .overlay-spinner {
width: 50px;
height: 50px;
border: 5px solid #f3f3f3;
border-top: 5px solid #3498db;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-bottom: 1rem;
}
/* Dark theme support */
@media (prefers-color-scheme: dark) {
.upload-overlay {
background: linear-gradient(135deg, rgba(30, 30, 30, 0.95), rgba(20, 20, 20, 0.95));
}
.upload-overlay h3 {
color: #fff;
}
.upload-overlay p {
color: #ccc;
}
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}

146
templates/partials/document_upload.html

@ -2,22 +2,96 @@
<h2>Document Uploads</h2>
<form id="upload-form" hx-post="/upload-documents" hx-encoding="multipart/form-data" hx-target="#upload-results"
hx-indicator="#upload-loading-indicator">
<!-- Job numbers will be added here by the CSV process -->
<div id="job-ids-container" style="display: none;">
<!-- Hidden input placeholder for job IDs -->
</div>
hx-indicator=".upload-overlay">
<div class="upload-container">
<!-- Upload overlay -->
<div class="upload-overlay htmx-indicator">
<div class="upload-overlay-content">
<div class="overlay-spinner"></div>
<h3>Uploading Documents</h3>
<p>Please wait while your documents are being uploaded...</p>
</div>
</div>
<!-- Job numbers will be added here by the CSV process -->
<div id="job-ids-container" style="display: none;">
<!-- Hidden input placeholder for job IDs -->
</div>
<!-- Step 1: CSV Upload -->
{{template "csv_upload" .}}
<div id="step1" class="content">
<h3 class="submenu-header">Step 1: Upload CSV file with Job IDs</h3>
<div>
<label>Select CSV file with job IDs:</label>
<input class="card-input" type="file" id="csv-file" name="csvFile" accept=".csv" required>
<button type="button" class="btn-primary" hx-post="/process-csv" hx-target="#job-ids-container"
hx-encoding="multipart/form-data" hx-include="#csv-file" hx-indicator="#csv-loading-indicator"
hx-on::after-request="if(event.detail.successful) { document.getElementById('step2').style.display = 'block'; }">
Upload CSV
</button>
<div id="csv-loading-indicator" class="htmx-indicator" style="display: none;">
<span>Processing CSV...</span>
<div class="loading-indicator"></div>
</div>
</div>
<div id="csv-preview" class="fade-me-out" style="display: none; margin-top: 1rem;">
<h4>Detected Jobs</h4>
<div id="csv-preview-content" class="job-list">
<!-- Job numbers will be displayed here -->
<p>No jobs loaded yet</p>
</div>
</div>
</div>
<!-- Step 2: Document Upload -->
{{template "document_upload_form" .}}
<div id="step2" class="content" style="display: none;">
<h3 class="submenu-header">Step 2: Upload Documents</h3>
<div id="document-upload-container">
<div class="document-row" id="document-row-1">
<div class="document-field">
<label>Select Document:</label>
<input class="card-input" type="file" id="document-file-1" name="document-file-1">
</div>
<div class="document-field-row">
<div class="document-field document-name-field">
<label>Document Name (optional):</label>
<input class="card-input" type="text" id="document-name-1" name="document-name-1"
placeholder="Document Name">
</div>
<div class="document-field document-type-field">
<label>Document Type:</label>
<select class="card-input" id="document-type-1" name="document-type-1">
<option value="">Select Document Type</option>
<option value="1" selected>Job Paperwork</option>
<option value="2">Job Vendor Bill</option>
<option value="4">Generic Attachment</option>
<option value="7">Blank Paperwork</option>
<option value="14">Job Invoice</option>
</select>
</div>
</div>
<button type="button" class="remove-document warning-button" hx-get="/document-field-remove?id=1"
hx-target="#document-row-1" hx-swap="outerHTML" style="display: none;">Remove</button>
</div>
</div>
<button type="button" id="add-document" class="caution-button" hx-get="/document-field-add"
hx-target="#document-upload-container" hx-swap="beforeend">Add Another Document</button>
<button type="button" class="btn-primary"
onclick="document.getElementById('step3').style.display = 'block';">
Continue to Step 3
</button>
</div>
<!-- Step 3: Submit -->
<div class="content">
<div id="step3" class="content" style="display: none;">
<h3 class="submenu-header">Step 3: Submit Uploads</h3>
<div>
<button type="submit" class="success-button" id="submit-button">Upload Documents to Jobs</button>
@ -30,6 +104,60 @@
<div id="upload-results" class="upload-results"></div>
</div>
</div>
<!-- Restart Button (initially hidden) -->
<div id="restart-section" class="content" style="display: none;">
<h3 class="submenu-header">Upload Complete</h3>
<button type="button" class="btn-primary" onclick="restartUpload()">Start New Upload</button>
</div>
</div>
</form>
<script>
// Function to restart the upload process
function restartUpload() {
// Reset form
document.getElementById('upload-form').reset();
// Hide all sections except step 1
document.getElementById('step2').style.display = 'none';
document.getElementById('step3').style.display = 'none';
document.getElementById('restart-section').style.display = 'none';
// Clear results
document.getElementById('upload-results').innerHTML = '';
document.getElementById('csv-preview-content').innerHTML = '<p>No jobs loaded yet</p>';
// Reset job IDs container
document.getElementById('job-ids-container').innerHTML = '';
// Show step 1
document.getElementById('step1').style.display = 'block';
}
// Add event listener for form submission
document.getElementById('upload-form').addEventListener('htmx:beforeRequest', function (evt) {
// Only show the overlay for document uploads, not CSV processing
if (evt.detail.pathInfo.requestPath === '/upload-documents') {
document.querySelector('.upload-overlay').style.display = 'flex';
}
});
document.getElementById('upload-form').addEventListener('htmx:afterRequest', function (evt) {
if (evt.detail.pathInfo.requestPath === '/upload-documents') {
if (evt.detail.successful) {
// Show restart section after successful upload
document.getElementById('restart-section').style.display = 'block';
}
// Hide the overlay after the request completes
document.querySelector('.upload-overlay').style.display = 'none';
}
});
// Add event listener for HTMX errors
document.getElementById('upload-form').addEventListener('htmx:error', function (evt) {
// Hide the overlay if there's an error
document.querySelector('.upload-overlay').style.display = 'none';
});
</script>
{{end}}
Loading…
Cancel
Save