|
<!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>Construction Resource Planner</title> |
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet"> |
|
<style> |
|
.phase-card { |
|
transition: transform 0.2s; |
|
} |
|
.phase-card:hover { |
|
transform: translateY(-5px); |
|
} |
|
.loading-spinner { |
|
border: 4px solid #f3f3f3; |
|
border-radius: 50%; |
|
border-top: 4px solid #3498db; |
|
width: 40px; |
|
height: 40px; |
|
animation: spin 1s linear infinite; |
|
} |
|
@keyframes spin { |
|
0% { transform: rotate(0deg); } |
|
100% { transform: rotate(360deg); } |
|
} |
|
.material-bar { |
|
height: 24px; |
|
background: linear-gradient(90deg, #3498db, #2980b9); |
|
transition: width 0.5s ease-in-out; |
|
} |
|
.drop-zone { |
|
border: 2px dashed #ccc; |
|
border-radius: 8px; |
|
padding: 20px; |
|
text-align: center; |
|
transition: border-color 0.3s ease; |
|
} |
|
.drop-zone.dragover { |
|
border-color: #3498db; |
|
background-color: rgba(52, 152, 219, 0.1); |
|
} |
|
.tab-active { |
|
border-bottom: 2px solid #3498db; |
|
color: #3498db; |
|
} |
|
</style> |
|
</head> |
|
<body class="bg-gray-50"> |
|
<nav class="bg-white shadow-lg"> |
|
<div class="max-w-7xl mx-auto px-4"> |
|
<div class="flex justify-between h-16"> |
|
<div class="flex items-center"> |
|
<img src="/api/placeholder/32/32" alt="Logo" class="h-8 w-8"> |
|
<span class="ml-2 text-xl font-semibold">Construction Resource Planner</span> |
|
</div> |
|
<div class="flex items-center space-x-4"> |
|
<button id="helpBtn" class="text-gray-600 hover:text-gray-900"> |
|
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /> |
|
</svg> |
|
</button> |
|
<div class="relative"> |
|
<img src="/api/placeholder/32/32" alt="User" class="h-8 w-8 rounded-full"> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</nav> |
|
|
|
<main class="max-w-7xl mx-auto px-4 py-8"> |
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-8"> |
|
|
|
<div class="bg-white rounded-lg shadow-md p-6"> |
|
<h2 class="text-2xl font-semibold mb-6">Project Details</h2> |
|
<form id="projectForm" class="space-y-6"> |
|
<div> |
|
<label class="block text-sm font-medium text-gray-700 mb-2"> |
|
Construction Type |
|
</label> |
|
<input type="text" id="constructionType" required |
|
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"> |
|
</div> |
|
|
|
<div> |
|
<label class="block text-sm font-medium text-gray-700 mb-2"> |
|
Number of Phases |
|
</label> |
|
<input type="number" id="phaseCount" min="1" required |
|
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"> |
|
</div> |
|
|
|
<div> |
|
<label class="block text-sm font-medium text-gray-700 mb-2"> |
|
Project Description |
|
</label> |
|
<textarea id="description" rows="4" required |
|
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"></textarea> |
|
</div> |
|
|
|
<div class="drop-zone" id="dropZone"> |
|
<div class="mb-4"> |
|
<svg class="mx-auto h-12 w-12 text-gray-400" stroke="currentColor" fill="none" viewBox="0 0 48 48"> |
|
<path d="M28 8H12a4 4 0 00-4 4v20m32-12v8m0 0v8a4 4 0 01-4 4H12a4 4 0 01-4-4v-4m32-4l-3.172-3.172a4 4 0 00-5.656 0L28 28M8 32l9.172-9.172a4 4 0 015.656 0L28 28m0 0l4 4m4-24h8m-4-4v8m-12 4h.02" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /> |
|
</svg> |
|
<div class="text-sm text-gray-600"> |
|
<label class="relative cursor-pointer bg-white rounded-md font-medium text-blue-600 hover:text-blue-500 focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-blue-500"> |
|
<span>Upload construction drawing</span> |
|
<input id="fileInput" type="file" class="sr-only" accept="image/*"> |
|
</label> |
|
</div> |
|
</div> |
|
<div id="preview" class="hidden mt-4"> |
|
<img id="imagePreview" class="max-w-full h-auto rounded-lg" alt="Preview"> |
|
</div> |
|
</div> |
|
|
|
<button type="submit" id="analyzeBtn" |
|
class="w-full bg-blue-600 text-white py-3 px-4 rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition-colors"> |
|
Analyze Project |
|
</button> |
|
</form> |
|
</div> |
|
|
|
|
|
<div class="bg-white rounded-lg shadow-md p-6"> |
|
<div class="flex justify-between items-center mb-6"> |
|
<h2 class="text-2xl font-semibold">Analysis Results</h2> |
|
<div class="flex space-x-4"> |
|
<button id="exportBtn" class="text-blue-600 hover:text-blue-700 hidden"> |
|
Export Report |
|
</button> |
|
</div> |
|
</div> |
|
|
|
<div id="loadingState" class="hidden flex flex-col items-center justify-center py-12"> |
|
<div class="loading-spinner mb-4"></div> |
|
<p class="text-gray-600">Analyzing project details...</p> |
|
</div> |
|
|
|
<div id="results" class="hidden space-y-6"> |
|
<div class="flex space-x-4 border-b"> |
|
<button class="tab-active px-4 py-2">Resources</button> |
|
<button class="px-4 py-2">Timeline</button> |
|
<button class="px-4 py-2">Dependencies</button> |
|
</div> |
|
|
|
<div id="phasesContainer" class="space-y-4"> |
|
|
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</main> |
|
|
|
<script> |
|
document.addEventListener('DOMContentLoaded', function() { |
|
const dropZone = document.getElementById('dropZone'); |
|
const fileInput = document.getElementById('fileInput'); |
|
const preview = document.getElementById('preview'); |
|
const imagePreview = document.getElementById('imagePreview'); |
|
const projectForm = document.getElementById('projectForm'); |
|
const loadingState = document.getElementById('loadingState'); |
|
const results = document.getElementById('results'); |
|
const exportBtn = document.getElementById('exportBtn'); |
|
const phasesContainer = document.getElementById('phasesContainer'); |
|
|
|
|
|
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { |
|
dropZone.addEventListener(eventName, preventDefaults, false); |
|
}); |
|
|
|
function preventDefaults(e) { |
|
e.preventDefault(); |
|
e.stopPropagation(); |
|
} |
|
|
|
['dragenter', 'dragover'].forEach(eventName => { |
|
dropZone.addEventListener(eventName, highlight, false); |
|
}); |
|
|
|
['dragleave', 'drop'].forEach(eventName => { |
|
dropZone.addEventListener(eventName, unhighlight, false); |
|
}); |
|
|
|
function highlight(e) { |
|
dropZone.classList.add('dragover'); |
|
} |
|
|
|
function unhighlight(e) { |
|
dropZone.classList.remove('dragover'); |
|
} |
|
|
|
dropZone.addEventListener('drop', handleDrop, false); |
|
|
|
function handleDrop(e) { |
|
const dt = e.dataTransfer; |
|
const files = dt.files; |
|
handleFiles(files); |
|
} |
|
|
|
fileInput.addEventListener('change', function() { |
|
handleFiles(this.files); |
|
}); |
|
|
|
function handleFiles(files) { |
|
if (files.length > 0) { |
|
const file = files[0]; |
|
if (file.type.startsWith('image/')) { |
|
const reader = new FileReader(); |
|
reader.onload = function(e) { |
|
preview.classList.remove('hidden'); |
|
imagePreview.src = e.target.result; |
|
} |
|
reader.readAsDataURL(file); |
|
} |
|
} |
|
} |
|
|
|
|
|
projectForm.addEventListener('submit', async function(e) { |
|
e.preventDefault(); |
|
|
|
loadingState.classList.remove('hidden'); |
|
results.classList.add('hidden'); |
|
|
|
|
|
setTimeout(() => { |
|
loadingState.classList.add('hidden'); |
|
results.classList.remove('hidden'); |
|
exportBtn.classList.remove('hidden'); |
|
|
|
|
|
const phases = [ |
|
{ |
|
phase: 1, |
|
materials: { |
|
'Concrete': { quantity: 1500, unit: 'cubic_meters' }, |
|
'Steel': { quantity: 250, unit: 'tons' } |
|
}, |
|
timeline: '3 months', |
|
dependencies: ['Site preparation', 'Foundation work'] |
|
}, |
|
{ |
|
phase: 2, |
|
materials: { |
|
'Glass': { quantity: 800, unit: 'square_meters' }, |
|
'Aluminum': { quantity: 120, unit: 'tons' } |
|
}, |
|
timeline: '2 months', |
|
dependencies: ['Phase 1 completion', 'Material delivery'] |
|
} |
|
]; |
|
|
|
renderPhases(phases); |
|
}, 2000); |
|
}); |
|
|
|
function renderPhases(phases) { |
|
phasesContainer.innerHTML = ''; |
|
phases.forEach(phase => { |
|
const phaseCard = document.createElement('div'); |
|
phaseCard.className = 'phase-card bg-gray-50 rounded-lg p-6'; |
|
|
|
let materialsHtml = ''; |
|
Object.entries(phase.materials).forEach(([material, info]) => { |
|
materialsHtml += ` |
|
<div class="mb-4"> |
|
<div class="flex justify-between mb-2"> |
|
<span>${material}</span> |
|
<span>${info.quantity} ${info.unit}</span> |
|
</div> |
|
<div class="bg-gray-200 rounded-full"> |
|
<div class="material-bar rounded-full" style="width: ${Math.min(info.quantity/20, 100)}%"></div> |
|
</div> |
|
</div> |
|
`; |
|
}); |
|
|
|
phaseCard.innerHTML = ` |
|
<h3 class="text-xl font-semibold mb-4">Phase ${phase.phase}</h3> |
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6"> |
|
<div> |
|
<h4 class="font-medium mb-3">Materials Required</h4> |
|
${materialsHtml} |
|
</div> |
|
<div> |
|
<h4 class="font-medium mb-3">Timeline & Dependencies</h4> |
|
<p class="mb-2">Duration: ${phase.timeline}</p> |
|
<ul class="list-disc list-inside"> |
|
${phase.dependencies.map(dep => `<li>${dep}</li>`).join('')} |
|
</ul> |
|
</div> |
|
</div> |
|
`; |
|
|
|
phasesContainer.appendChild(phaseCard); |
|
}); |
|
} |
|
|
|
|
|
exportBtn.addEventListener('click', function() { |
|
|
|
alert('Generating report...'); |
|
}); |
|
}); |
|
</script> |
|
</body> |
|
</html> |