|
|
|
@ -8,6 +8,7 @@ import ( |
|
|
|
"log" |
|
|
|
"mime/multipart" |
|
|
|
"net/http" |
|
|
|
"path/filepath" |
|
|
|
"regexp" |
|
|
|
"strconv" |
|
|
|
"strings" |
|
|
|
@ -94,42 +95,100 @@ func ProcessCSVHandler(w http.ResponseWriter, r *http.Request) { |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
if len(csvData) < 2 { |
|
|
|
http.Error(w, "CSV file must contain at least a header row and one data row", http.StatusBadRequest) |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
// Find the index of the 'id' column
|
|
|
|
headerRow := csvData[0] |
|
|
|
idColumnIndex := -1 |
|
|
|
for i, header := range headerRow { |
|
|
|
if strings.ToLower(strings.TrimSpace(header)) == "id" { |
|
|
|
idColumnIndex = i |
|
|
|
break |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// If 'id' column not found, try the first column
|
|
|
|
if idColumnIndex == -1 { |
|
|
|
idColumnIndex = 0 |
|
|
|
log.Printf("No 'id' column found in CSV, using first column (header: %s)", headerRow[0]) |
|
|
|
} else { |
|
|
|
log.Printf("Found 'id' column at index %d", idColumnIndex) |
|
|
|
} |
|
|
|
|
|
|
|
// Extract job numbers from the CSV
|
|
|
|
var jobNumbers []string |
|
|
|
for _, row := range csvData { |
|
|
|
if len(row) > 0 && row[0] != "" { |
|
|
|
// Trim whitespace and skip headers or empty lines
|
|
|
|
jobNum := strings.TrimSpace(row[0]) |
|
|
|
if jobNum != "Job Number" && jobNum != "JobNumber" && jobNum != "Job" && jobNum != "" { |
|
|
|
jobNumbers = append(jobNumbers, jobNum) |
|
|
|
for rowIndex, row := range csvData { |
|
|
|
// Skip header row
|
|
|
|
if rowIndex == 0 { |
|
|
|
continue |
|
|
|
} |
|
|
|
|
|
|
|
if len(row) > idColumnIndex { |
|
|
|
// Extract and clean up the job ID
|
|
|
|
jobID := strings.TrimSpace(row[idColumnIndex]) |
|
|
|
if jobID != "" { |
|
|
|
jobNumbers = append(jobNumbers, jobID) |
|
|
|
log.Printf("Added job ID: %s", jobID) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
log.Printf("Extracted %d job IDs from CSV", len(jobNumbers)) |
|
|
|
|
|
|
|
// Validate each job number (optional: could make API calls to verify they exist)
|
|
|
|
// Create a list of valid job numbers
|
|
|
|
var validJobNumbers []string |
|
|
|
validJobNumbers = append(validJobNumbers, jobNumbers...) |
|
|
|
|
|
|
|
// Generate HTML for job list
|
|
|
|
var jobListHTML string |
|
|
|
if len(validJobNumbers) > 0 { |
|
|
|
// Add a hidden input with comma-separated job IDs for form submission
|
|
|
|
jobListHTML = fmt.Sprintf("<input type='hidden' name='jobNumbers' value='%s'>", strings.Join(validJobNumbers, ",")) |
|
|
|
jobListHTML += "<ul class='job-list'>" |
|
|
|
for _, jobNum := range validJobNumbers { |
|
|
|
jobListHTML += fmt.Sprintf("<li data-job-id='%s'>Job #%s</li>", jobNum, jobNum) |
|
|
|
} |
|
|
|
jobListHTML += "</ul>" |
|
|
|
// Add JavaScript to make the CSV preview visible
|
|
|
|
jobListHTML += "<script>document.getElementById('csv-preview').style.display = 'block';</script>" |
|
|
|
// Create a hidden input with the job IDs
|
|
|
|
jobsValue := strings.Join(validJobNumbers, ",") |
|
|
|
|
|
|
|
// Insert a hidden input for job numbers and show the job list
|
|
|
|
jobListHTML = fmt.Sprintf(` |
|
|
|
<input type="hidden" name="jobNumbers" value="%s"> |
|
|
|
|
|
|
|
<style> |
|
|
|
#csv-preview { display: block !important; } |
|
|
|
</style> |
|
|
|
|
|
|
|
<script> |
|
|
|
// Update the job list display
|
|
|
|
document.getElementById("csv-preview-content").innerHTML = ''; |
|
|
|
var ul = document.createElement("ul"); |
|
|
|
ul.className = "job-list"; |
|
|
|
%s |
|
|
|
document.getElementById("csv-preview-content").appendChild(ul); |
|
|
|
</script> |
|
|
|
`, jobsValue, buildJobListJS(validJobNumbers)) |
|
|
|
} else { |
|
|
|
jobListHTML = "<p>No valid job numbers found in the CSV file.</p>" |
|
|
|
jobListHTML = ` |
|
|
|
<p>No valid job numbers found in the CSV file.</p> |
|
|
|
` |
|
|
|
} |
|
|
|
|
|
|
|
w.Header().Set("Content-Type", "text/html") |
|
|
|
w.Write([]byte(jobListHTML)) |
|
|
|
} |
|
|
|
|
|
|
|
// Helper function to build JavaScript for job list
|
|
|
|
func buildJobListJS(jobIDs []string) string { |
|
|
|
var js strings.Builder |
|
|
|
for _, id := range jobIDs { |
|
|
|
js.WriteString(fmt.Sprintf(` |
|
|
|
var li = document.createElement("li"); |
|
|
|
li.setAttribute("data-job-id", "%s"); |
|
|
|
li.textContent = "Job #%s"; |
|
|
|
ul.appendChild(li); |
|
|
|
`, id, id)) |
|
|
|
} |
|
|
|
return js.String() |
|
|
|
} |
|
|
|
|
|
|
|
// UploadDocumentsHandler handles document uploads to jobs
|
|
|
|
func UploadDocumentsHandler(w http.ResponseWriter, r *http.Request) { |
|
|
|
session, ok := r.Context().Value("session").(*api.Session) |
|
|
|
@ -146,16 +205,24 @@ func UploadDocumentsHandler(w http.ResponseWriter, r *http.Request) { |
|
|
|
|
|
|
|
// Parse the multipart form with a 30MB limit
|
|
|
|
if err := r.ParseMultipartForm(30 << 20); err != nil { |
|
|
|
http.Error(w, "Unable to parse form: "+err.Error(), http.StatusBadRequest) |
|
|
|
http.Error(w, fmt.Sprintf("Unable to parse form: %v", err), http.StatusBadRequest) |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
// Get the job numbers
|
|
|
|
// Get the job numbers from either of the possible form fields
|
|
|
|
jobNumbers := r.FormValue("jobNumbers") |
|
|
|
if jobNumbers == "" { |
|
|
|
jobNumbers = r.FormValue("job-ids") |
|
|
|
if jobNumbers == "" { |
|
|
|
log.Printf("No job numbers provided. Form data: %+v", r.Form) |
|
|
|
http.Error(w, "No job numbers provided", http.StatusBadRequest) |
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Log the form data for debugging
|
|
|
|
log.Printf("Form data: %+v", r.Form) |
|
|
|
log.Printf("Job numbers: %s", jobNumbers) |
|
|
|
|
|
|
|
// Split the job numbers
|
|
|
|
jobs := strings.Split(jobNumbers, ",") |
|
|
|
@ -210,7 +277,17 @@ func UploadDocumentsHandler(w http.ResponseWriter, r *http.Request) { |
|
|
|
documentName := r.FormValue(nameKey) |
|
|
|
if documentName == "" { |
|
|
|
documentName = fileHeader.Filename |
|
|
|
} else { |
|
|
|
// If a custom name is provided without extension, add the original file extension
|
|
|
|
if !strings.Contains(documentName, ".") { |
|
|
|
extension := filepath.Ext(fileHeader.Filename) |
|
|
|
if extension != "" { |
|
|
|
documentName = documentName + extension |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
log.Printf("Using document name: %s (original filename: %s)", documentName, fileHeader.Filename) |
|
|
|
|
|
|
|
// Get document type
|
|
|
|
documentType := r.FormValue(typeKey) |
|
|
|
@ -219,6 +296,9 @@ func UploadDocumentsHandler(w http.ResponseWriter, r *http.Request) { |
|
|
|
continue |
|
|
|
} |
|
|
|
|
|
|
|
// Log the document type for debugging
|
|
|
|
log.Printf("Document type for %s: '%s'", documentName, documentType) |
|
|
|
|
|
|
|
documents = append(documents, DocumentData{ |
|
|
|
File: file, |
|
|
|
Header: fileHeader, |
|
|
|
@ -275,6 +355,9 @@ func UploadDocumentsHandler(w http.ResponseWriter, r *http.Request) { |
|
|
|
resultHTML.WriteString("<div class='upload-results'>") |
|
|
|
resultHTML.WriteString("<h4>Upload Results</h4>") |
|
|
|
|
|
|
|
if len(results) == 0 { |
|
|
|
resultHTML.WriteString("<p class='error'>No documents were uploaded. Please check that you have selected files and document types.</p>") |
|
|
|
} else { |
|
|
|
for jobID, jobResults := range results { |
|
|
|
resultHTML.WriteString(fmt.Sprintf("<div class='job-result'><h5>Job #%s</h5><ul>", jobID)) |
|
|
|
|
|
|
|
@ -292,9 +375,13 @@ func UploadDocumentsHandler(w http.ResponseWriter, r *http.Request) { |
|
|
|
|
|
|
|
resultHTML.WriteString("</ul></div>") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
resultHTML.WriteString("</div>") |
|
|
|
|
|
|
|
// Add JavaScript to scroll to results
|
|
|
|
resultHTML.WriteString("<script>document.getElementById('upload-results').scrollIntoView({behavior: 'smooth'});</script>") |
|
|
|
|
|
|
|
w.Header().Set("Content-Type", "text/html") |
|
|
|
w.Write(resultHTML.Bytes()) |
|
|
|
} |
|
|
|
@ -323,16 +410,10 @@ func DocumentFieldAddHandler(w http.ResponseWriter, r *http.Request) { |
|
|
|
<label>Document Type:</label> |
|
|
|
<select class="card-input" id="document-type-%s" name="document-type-%s"> |
|
|
|
<option value="">Select Document Type</option> |
|
|
|
<option value="1">Job Paperwork</option> |
|
|
|
<option value="2">Job Vendor Bill</option> |
|
|
|
<option value="3">Job Quality Control Picture</option> |
|
|
|
<option value="5">Deficiency Repair Proposal</option> |
|
|
|
<option value="7">Generic Attachment</option> |
|
|
|
<option value="8">Avatar Image</option> |
|
|
|
<option value="9">Import</option> |
|
|
|
<option value="01" selected>Job Paperwork</option> |
|
|
|
<option value="02">Job Vendor Bill</option> |
|
|
|
<option value="07">Generic Attachment</option> |
|
|
|
<option value="10">Blank Paperwork</option> |
|
|
|
<option value="11">Work Acknowledgement</option> |
|
|
|
<option value="12">Logo</option> |
|
|
|
<option value="14">Job Invoice</option> |
|
|
|
</select> |
|
|
|
</div> |
|
|
|
|