Initial commit - Stage 1 working version
Saving current working state before proceeding to Stage 2. Includes: - Backend: Python-based QC validator with shapefile processing - Frontend: Drag-and-drop file upload interface - Sample files for testing - Documentation and revision history 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 2.0 MiB |
@@ -0,0 +1,28 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Verofy Shapefile Upload</title>
|
||||
<link rel="stylesheet" href="style.css?v=3">
|
||||
</head>
|
||||
<body>
|
||||
<div class="main-container">
|
||||
<div class="logo-section">
|
||||
<img src="logo.png" alt="Vero Dragndrop HLD Logo" class="logo">
|
||||
</div>
|
||||
<div class="upload-section">
|
||||
<div class="container">
|
||||
<h1>Verofy HLD Shapefile Upload</h1>
|
||||
<div id="drop-area">
|
||||
<p>Drag & Drop your ZIP file here</p>
|
||||
<input type="file" id="fileElem" accept=".zip" />
|
||||
</div>
|
||||
<div id="result"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="upload.js?v=3"></script>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.2 MiB |
@@ -0,0 +1,114 @@
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.main-container {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.logo-section {
|
||||
flex: 0 0 45%;
|
||||
background: linear-gradient(135deg, #001f3f 0%, #003366 100%);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 60px;
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 85%;
|
||||
height: auto;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.upload-section {
|
||||
flex: 0 0 55%;
|
||||
background: #e8e8e8;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.container {
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
width: 100%;
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #333;
|
||||
margin-bottom: 30px;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
#drop-area {
|
||||
border: 2px dashed #007BFF;
|
||||
border-radius: 10px;
|
||||
width: 100%;
|
||||
min-height: 200px;
|
||||
margin: 20px auto;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
background: #fff;
|
||||
cursor: pointer;
|
||||
padding: 40px 20px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
#drop-area:hover {
|
||||
border-color: #0056b3;
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
#drop-area.highlight {
|
||||
border-color: #0056b3;
|
||||
background: #e7f3ff;
|
||||
}
|
||||
|
||||
#drop-area p {
|
||||
margin-bottom: 15px;
|
||||
color: #666;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.button {
|
||||
margin-top: 10px;
|
||||
background: #007BFF;
|
||||
color: #fff;
|
||||
padding: 10px 20px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
}
|
||||
|
||||
#result {
|
||||
margin-top: 20px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Responsive design */
|
||||
@media (max-width: 768px) {
|
||||
.main-container {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.logo-section {
|
||||
flex: 0 0 30%;
|
||||
}
|
||||
|
||||
.upload-section {
|
||||
flex: 0 0 70%;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
const dropArea = document.getElementById('drop-area')
|
||||
const fileInput = document.getElementById('fileElem')
|
||||
|
||||
// Prevent default drag behaviors
|
||||
;['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
|
||||
dropArea.addEventListener(eventName, preventDefaults, false)
|
||||
})
|
||||
|
||||
function preventDefaults(e) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
}
|
||||
|
||||
// Highlight drop area on dragover
|
||||
;['dragenter', 'dragover'].forEach(eventName => {
|
||||
dropArea.addEventListener(eventName, () => dropArea.classList.add('highlight'), false)
|
||||
})
|
||||
|
||||
;['dragleave', 'drop'].forEach(eventName => {
|
||||
dropArea.addEventListener(eventName, () => dropArea.classList.remove('highlight'), false)
|
||||
})
|
||||
|
||||
// Handle dropped files
|
||||
dropArea.addEventListener('drop', e => {
|
||||
const dt = e.dataTransfer
|
||||
const files = dt.files
|
||||
handleFiles(files)
|
||||
})
|
||||
|
||||
// Handle selected files from input
|
||||
fileInput.addEventListener('change', e => {
|
||||
handleFiles(fileInput.files)
|
||||
})
|
||||
|
||||
function handleFiles(files) {
|
||||
if (!files || files.length === 0) return
|
||||
const file = files[0]
|
||||
|
||||
if (!file.name.endsWith('.zip')) {
|
||||
alert('Please upload a ZIP file.')
|
||||
return
|
||||
}
|
||||
|
||||
uploadFile(file)
|
||||
}
|
||||
|
||||
function uploadFile(file) {
|
||||
const url = 'http://localhost:8000/upload'
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
|
||||
dropArea.innerHTML = `<p>Uploading ${file.name}...</p>`
|
||||
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(async response => {
|
||||
const contentType = response.headers.get('Content-Type')
|
||||
|
||||
// Check if content type includes text/plain (for QC failure report)
|
||||
if (contentType && contentType.includes('text/plain')) {
|
||||
// QC Failed - download report
|
||||
const blob = await response.blob()
|
||||
const link = document.createElement('a')
|
||||
link.href = window.URL.createObjectURL(blob)
|
||||
link.download = 'QC_report.txt'
|
||||
link.click()
|
||||
dropArea.innerHTML = `<p>QC failed. Report downloaded.</p>`
|
||||
} else if (contentType && contentType.includes('application/json')) {
|
||||
// QC Passed - show success and VerofyMapID input
|
||||
const data = await response.json()
|
||||
if (data.message === 'success') {
|
||||
showVerofyMapIdInput()
|
||||
} else {
|
||||
dropArea.innerHTML = `<p>Unexpected response from server.</p>`
|
||||
}
|
||||
} else {
|
||||
// Unknown response type, try to handle as text
|
||||
const text = await response.text()
|
||||
dropArea.innerHTML = `<p>Unexpected response: ${text.substring(0, 100)}</p>`
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Upload error:', error)
|
||||
dropArea.innerHTML = `<p>Upload failed. Check console.</p>`
|
||||
})
|
||||
}
|
||||
|
||||
function showVerofyMapIdInput() {
|
||||
dropArea.innerHTML = `
|
||||
<div style="padding: 20px;">
|
||||
<p style="color: green; font-weight: bold; margin-bottom: 15px;">
|
||||
Your files have passed QC!
|
||||
</p>
|
||||
<p style="margin-bottom: 10px;">Please provide VerofyMapID:</p>
|
||||
<input type="number" id="verofyMapId" placeholder="Enter Map ID"
|
||||
style="padding: 8px; width: 200px; margin-bottom: 10px; border: 1px solid #ccc; border-radius: 4px;" />
|
||||
<br/>
|
||||
<button onclick="submitMapId()"
|
||||
style="padding: 10px 20px; background: #007BFF; color: white; border: none; border-radius: 5px; cursor: pointer;">
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
function submitMapId() {
|
||||
const mapIdInput = document.getElementById('verofyMapId')
|
||||
const mapId = mapIdInput.value
|
||||
|
||||
if (!mapId || mapId.trim() === '') {
|
||||
alert('Please enter a VerofyMapID')
|
||||
return
|
||||
}
|
||||
|
||||
// Update the drop area to show success message
|
||||
dropArea.innerHTML = `
|
||||
<p style="color: green; font-weight: bold;">
|
||||
Success! VerofyMapID ${mapId} received.
|
||||
</p>
|
||||
`
|
||||
|
||||
// Create overlay with celebration image
|
||||
const overlay = document.createElement('div')
|
||||
overlay.id = 'celebrationOverlay'
|
||||
overlay.style.cssText = `
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 10000;
|
||||
cursor: pointer;
|
||||
`
|
||||
|
||||
overlay.innerHTML = `
|
||||
<img src="celebrate.png" alt="Celebration"
|
||||
style="max-width: 80%; max-height: 80%; object-fit: contain;" />
|
||||
`
|
||||
|
||||
// Remove overlay on click
|
||||
overlay.addEventListener('click', () => {
|
||||
overlay.remove()
|
||||
})
|
||||
|
||||
document.body.appendChild(overlay)
|
||||
}
|
||||
Reference in New Issue
Block a user