<!
DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>CBE Construction Project Management</title>
<!-- Dependencies -->
<script src="[Link]
<link rel="stylesheet" href="[Link]
awesome/6.4.0/css/[Link]">
<script src="[Link]
<script
src="[Link]
<script src="[Link]
<link rel="stylesheet" href="[Link]
<script src="[Link]
<style id="app-style">
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: #f8f9fa;
}
.sidebar {
width: 250px;
transition: all 0.3s;
}
.sidebar-collapsed {
width: 80px;
}
.content-area {
transition: all 0.3s;
}
.nav-item {
border-left: 4px solid transparent;
transition: all 0.2s;
}
.[Link] {
border-left: 4px solid #3c82f6;
background-color: rgba(59, 130, 246, 0.1);
}
.nav-item:hover:not(.active) {
background-color: rgba(59, 130, 246, 0.05);
}
.module {
display: none;
}
.[Link] {
display: block;
}
.status-badge {
padding: 2px 8px;
border-radius: 12px;
font-size: 0.75rem;
font-weight: 600;
}
.status-badge-success {
background-color: rgba(16, 185, 129, 0.2);
color: rgb(6, 95, 70);
}
.status-badge-warning {
background-color: rgba(245, 158, 11, 0.2);
color: rgb(146, 64, 14);
}
.status-badge-danger {
background-color: rgba(239, 68, 68, 0.2);
color: rgb(153, 27, 27);
}
.status-badge-info {
background-color: rgba(59, 130, 246, 0.2);
color: rgb(30, 64, 175);
}
.progress-container {
height: 8px;
width: 100%;
background-color: #e5e7eb;
border-radius: 4px;
overflow: hidden;
}
.progress-bar {
height: 100%;
border-radius: 4px;
transition: width 0.6s ease;
}
.progress-bar-success {
background-color: #10b981;
}
.progress-bar-warning {
background-color: #f59e0b;
}
.progress-bar-danger {
background-color: #ef4444;
}
.timeline-container {
position: relative;
padding-left: 30px;
}
.timeline-item {
position: relative;
padding-bottom: 20px;
}
.timeline-item::before {
content: "";
position: absolute;
left: -30px;
top: 0;
height: 100%;
width: 2px;
background-color: #e5e7eb;
}
.timeline-item::after {
content: "";
position: absolute;
left: -36px;
top: 0;
height: 12px;
width: 12px;
border-radius: 50%;
background-color: #3c82f6;
}
.timeline-item:last-child::before {
height: 0;
}
.card {
background-color: white;
border-radius: 8px;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
transition: transform 0.2s, box-shadow 0.2s;
}
.card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
}
.map-container {
height: 600px;
z-index: 1;
}
.annotation-canvas {
border: 1px solid #ccc;
cursor: crosshair;
}
.file-drop-area {
border: 2px dashed #ccc;
border-radius: 8px;
padding: 2rem;
text-align: center;
transition: all 0.3s;
}
.file-drop-area:hover, .[Link] {
border-color: #3c82f6;
background-color: rgba(59, 130, 246, 0.05);
}
/* Custom select styling */
.custom-select {
appearance: none;
background-image: url("data:image/svg+xml,%3csvg
xmlns='[Link] fill='none' viewBox='0 0 20 20'%3e%3cpath
stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5'
d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
background-position: right 0.5rem center;
background-repeat: no-repeat;
background-size: 1.5em 1.5em;
padding-right: 2.5rem;
}
/* Gantt chart styles */
.gantt-container {
overflow-x: auto;
}
.gantt-row {
height: 40px;
border-bottom: 1px solid #e5e7eb;
}
.gantt-bar {
height: 24px;
margin-top: 8px;
border-radius: 4px;
position: relative;
}
.gantt-bar-inner {
position: absolute;
left: 0;
top: 0;
height: 100%;
background-color: #3c82f6;
border-radius: 4px;
}
.gantt-milestone {
width: 12px;
height: 12px;
background-color: #ef4444;
border-radius: 50%;
margin-top: 14px;
}
</style>
</head>
<body class="min-h-screen">
<div class="flex h-screen overflow-hidden">
<!-- Sidebar -->
<aside id="sidebar" class="sidebar bg-white border-r border-gray-200 h-full flex flex-col
shadow-sm">
<!-- Logo -->
<div class="p-4 flex items-center justify-center">
<span class="text-xl font-bold text-blue-600">CBE Projects</span>
<button id="toggle-sidebar" class="ml-auto text-gray-500 hover:text-gray-700">
<i class="fas fa-bars"></i>
</button>
</div>
<!-- Navigation -->
<nav class="mt-4 flex-1">
<ul class="space-y-1 px-2">
<li class="nav-item active" data-module="dashboard">
<a href="javascript:void(0)" class="flex items-center p-3 text-gray-700 rounded-lg">
<i class="fas fa-chart-line text-blue-600"></i>
<span class="ml-3 sidebar-text">Project Dashboard</span>
</a>
</li>
<li class="nav-item" data-module="documents">
<a href="javascript:void(0)" class="flex items-center p-3 text-gray-700 rounded-lg">
<i class="fas fa-file-alt text-blue-600"></i>
<span class="ml-3 sidebar-text">Document Management</span>
</a>
</li>
<li class="nav-item" data-module="issues">
<a href="javascript:void(0)" class="flex items-center p-3 text-gray-700 rounded-lg">
<i class="fas fa-exclamation-triangle text-blue-600"></i>
<span class="ml-3 sidebar-text">Issue Reporting</span>
</a>
</li>
<li class="nav-item" data-module="map">
<a href="javascript:void(0)" class="flex items-center p-3 text-gray-700 rounded-lg">
<i class="fas fa-map-marker-alt text-blue-600"></i>
<span class="ml-3 sidebar-text">GIS Map</span>
</a>
</li>
<li class="nav-item" data-module="resources">
<a href="javascript:void(0)" class="flex items-center p-3 text-gray-700 rounded-lg">
<i class="fas fa-coins text-blue-600"></i>
<span class="ml-3 sidebar-text">Resource & Cost</span>
</a>
</li>
<li class="nav-item" data-module="design">
<a href="javascript:void(0)" class="flex items-center p-3 text-gray-700 rounded-lg">
<i class="fas fa-drafting-compass text-blue-600"></i>
<span class="ml-3 sidebar-text">Design Review</span>
</a>
</li>
</ul>
</nav>
<!-- User Info -->
<div class="p-4 border-t border-gray-200 flex items-center">
<div class="w-8 h-8 rounded-full bg-blue-500 flex items-center justify-center text-
white">
<span>JD</span>
</div>
<div class="ml-3 sidebar-text">
<p class="text-sm font-medium text-gray-700">John Doe</p>
<p class="text-xs text-gray-500">Project Manager</p>
</div>
</div>
</aside>
<!-- Main Content -->
<div class="content-area flex-1 overflow-y-auto bg-gray-50">
<!-- Header -->
<header class="bg-white shadow-sm">
<div class="px-6 py-4">
<h1 id="module-title" class="text-2xl font-semibold text-gray-800">Project
Dashboard</h1>
<p id="module-description" class="text-gray-500">Track and manage all ongoing
construction projects</p>
</div>
</header>
<!-- Module Content -->
<div class="p-6">
<!-- Project Dashboard Module -->
<div id="dashboard-module" class="module active">
<!-- View Toggle -->
<div class="flex justify-between items-center mb-6">
<div class="flex space-x-2">
<button id="card-view-btn" class="px-4 py-2 bg-blue-600 text-white rounded-lg
shadow-sm">
<i class="fas fa-th-large mr-2"></i> Card View
</button>
<button id="gantt-view-btn" class="px-4 py-2 bg-white text-gray-700 rounded-lg
shadow-sm">
<i class="fas fa-bars mr-2"></i> Gantt View
</button>
</div>
<button id="add-project-btn" class="px-4 py-2 bg-green-600 text-white rounded-lg
shadow-sm flex items-center">
<i class="fas fa-plus mr-2"></i> New Project
</button>
</div>
<!-- Card View -->
<div id="card-view" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<!-- Harar Branch Project -->
<div class="card p-5">
<div class="flex justify-between items-start">
<h3 class="text-lg font-semibold text-gray-800">Harar Branch Construction</h3>
<span class="status-badge status-badge-warning">At Risk</span>
</div>
<p class="text-gray-600 mt-2">New branch office building in Harar city center</p>
<div class="mt-4">
<div class="flex justify-between mb-1">
<span class="text-sm text-gray-600">Progress</span>
<span class="text-sm font-medium text-gray-800">65%</span>
</div>
<div class="progress-container">
<div class="progress-bar progress-bar-warning" style="width: 65%"></div>
</div>
</div>
<div class="mt-4 flex justify-between text-sm text-gray-600">
<span>Start: Jan 15, 2025</span>
<span>End: Sep 30, 2025</span>
</div>
<div class="mt-4 flex items-center">
<span class="text-sm text-gray-600 mr-2">Manager:</span>
<span class="text-sm font-medium text-gray-800">Abebe Kebede</span>
</div>
<div class="mt-4 flex justify-between">
<button class="px-3 py-1.5 text-sm bg-blue-600 text-white rounded">Manage
Tasks</button>
<button class="px-3 py-1.5 text-sm bg-white border border-gray-300 text-gray-700
rounded">View Details</button>
</div>
</div>
<!-- Adama Branch Project -->
<div class="card p-5">
<div class="flex justify-between items-start">
<h3 class="text-lg font-semibold text-gray-800">Adama Branch Renovation</h3>
<span class="status-badge status-badge-success">On Track</span>
</div>
<p class="text-gray-600 mt-2">Renovation and expansion of existing branch</p>
<div class="mt-4">
<div class="flex justify-between mb-1">
<span class="text-sm text-gray-600">Progress</span>
<span class="text-sm font-medium text-gray-800">82%</span>
</div>
<div class="progress-container">
<div class="progress-bar progress-bar-success" style="width: 82%"></div>
</div>
</div>
<div class="mt-4 flex justify-between text-sm text-gray-600">
<span>Start: Nov 5, 2024</span>
<span>End: Mar 15, 2025</span>
</div>
<div class="mt-4 flex items-center">
<span class="text-sm text-gray-600 mr-2">Manager:</span>
<span class="text-sm font-medium text-gray-800">Tigist Alemu</span>
</div>
<div class="mt-4 flex justify-between">
<button class="px-3 py-1.5 text-sm bg-blue-600 text-white rounded">Manage
Tasks</button>
<button class="px-3 py-1.5 text-sm bg-white border border-gray-300 text-gray-700
rounded">View Details</button>
</div>
</div>
<!-- CBC Headquarters Project -->
<div class="card p-5">
<div class="flex justify-between items-start">
<h3 class="text-lg font-semibold text-gray-800">CBC Headquarters</h3>
<span class="status-badge status-badge-danger">Delayed</span>
</div>
<p class="text-gray-600 mt-2">New corporate headquarters building in Addis</p>
<div class="mt-4">
<div class="flex justify-between mb-1">
<span class="text-sm text-gray-600">Progress</span>
<span class="text-sm font-medium text-gray-800">35%</span>
</div>
<div class="progress-container">
<div class="progress-bar progress-bar-danger" style="width: 35%"></div>
</div>
</div>
<div class="mt-4 flex justify-between text-sm text-gray-600">
<span>Start: Mar 1, 2024</span>
<span>End: Dec 30, 2025</span>
</div>
<div class="mt-4 flex items-center">
<span class="text-sm text-gray-600 mr-2">Manager:</span>
<span class="text-sm font-medium text-gray-800">Solomon Teshome</span>
</div>
<div class="mt-4 flex justify-between">
<button class="px-3 py-1.5 text-sm bg-blue-600 text-white rounded">Manage
Tasks</button>
<button class="px-3 py-1.5 text-sm bg-white border border-gray-300 text-gray-700
rounded">View Details</button>
</div>
</div>
</div>
<!-- Gantt View (Hidden by default) -->
<div id="gantt-view" class="hidden bg-white rounded-lg shadow-sm p-4">
<div class="gantt-container">
<div class="flex mb-4 border-b border-gray-200 pb-2">
<div class="w-1/4 font-medium text-gray-700">Project</div>
<div class="w-3/4 flex">
<div class="w-1/12 text-center text-sm text-gray-600">Jan</div>
<div class="w-1/12 text-center text-sm text-gray-600">Feb</div>
<div class="w-1/12 text-center text-sm text-gray-600">Mar</div>
<div class="w-1/12 text-center text-sm text-gray-600">Apr</div>
<div class="w-1/12 text-center text-sm text-gray-600">May</div>
<div class="w-1/12 text-center text-sm text-gray-600">Jun</div>
<div class="w-1/12 text-center text-sm text-gray-600">Jul</div>
<div class="w-1/12 text-center text-sm text-gray-600">Aug</div>
<div class="w-1/12 text-center text-sm text-gray-600">Sep</div>
<div class="w-1/12 text-center text-sm text-gray-600">Oct</div>
<div class="w-1/12 text-center text-sm text-gray-600">Nov</div>
<div class="w-1/12 text-center text-sm text-gray-600">Dec</div>
</div>
</div>
<!-- Gantt chart rows -->
<div class="gantt-row flex">
<div class="w-1/4 flex items-center text-gray-800">Harar Branch
Construction</div>
<div class="w-3/4 relative">
<div class="gantt-bar absolute" style="left: 4%; width: 67%">
<div class="gantt-bar-inner" style="width: 65%"></div>
</div>
</div>
</div>
<div class="gantt-row flex">
<div class="w-1/4 flex items-center text-gray-800">Adama Branch
Renovation</div>
<div class="w-3/4 relative">
<div class="gantt-bar absolute" style="left: 84%; width: 25%">
<div class="gantt-bar-inner" style="width: 82%; background-color:
#10b981;"></div>
</div>
</div>
</div>
<div class="gantt-row flex">
<div class="w-1/4 flex items-center text-gray-800">CBC Headquarters</div>
<div class="w-3/4 relative">
<div class="gantt-bar absolute" style="left: 17%; width: 92%">
<div class="gantt-bar-inner" style="width: 35%; background-color:
#ef4444;"></div>
</div>
<div class="gantt-milestone absolute" style="left: 50%"></div>
</div>
</div>
</div>
</div>
<!-- Recent Activities -->
<div class="mt-8">
<h2 class="text-xl font-semibold text-gray-800 mb-4">Recent Activities</h2>
<div class="bg-white rounded-lg shadow-sm p-4">
<div class="timeline-container">
<div class="timeline-item">
<p class="text-sm text-gray-500">Today, 10:45 AM</p>
<p class="font-medium">Material delivery confirmed for Harar Branch</p>
<p class="text-gray-600">Steel reinforcement arrived at the construction site</p>
</div>
<div class="timeline-item">
<p class="text-sm text-gray-500">Yesterday, 3:20 PM</p>
<p class="font-medium">Safety inspection completed at CBC Headquarters</p>
<p class="text-gray-600">Minor issues identified, report available in
documents</p>
</div>
<div class="timeline-item">
<p class="text-sm text-gray-500">Feb 14, 2025</p>
<p class="font-medium">Adama Branch renovation milestone achieved</p>
<p class="text-gray-600">Interior finishing phase completed ahead of
schedule</p>
</div>
</div>
</div>
</div>
</div>
<!-- Document Management Module -->
<div id="documents-module" class="module">
<div class="flex justify-between items-center mb-6">
<div class="w-1/2">
<div class="relative">
<input type="text" placeholder="Search documents..." class="w-full px-4 py-2
border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
<i class="fas fa-search absolute right-3 top-3 text-gray-400"></i>
</div>
</div>
<div class="flex space-x-2">
<select class="custom-select px-4 py-2 border rounded-lg">
<option value="">All Projects</option>
<option value="harar">Harar Branch</option>
<option value="adama">Adama Branch</option>
<option value="cbc">CBC Headquarters</option>
</select>
<select class="custom-select px-4 py-2 border rounded-lg">
<option value="">All Document Types</option>
<option value="blueprint">Blueprints</option>
<option value="contract">Contracts</option>
<option value="report">Reports</option>
<option value="approval">Approvals</option>
</select>
<button id="upload-doc-btn" class="px-4 py-2 bg-blue-600 text-white rounded-lg
shadow-sm flex items-center">
<i class="fas fa-upload mr-2"></i> Upload
</button>
</div>
</div>
<div class="bg-white rounded-lg shadow-sm overflow-hidden">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500
uppercase tracking-wider">Document Name</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500
uppercase tracking-wider">Project</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500
uppercase tracking-wider">Type</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500
uppercase tracking-wider">Version</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500
uppercase tracking-wider">Uploaded By</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500
uppercase tracking-wider">Date</th>
<th scope="col" class="px-6 py-3 text-right text-xs font-medium text-gray-500
uppercase tracking-wider">Actions</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
<tr>
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center">
<i class="fas fa-file-pdf text-red-500 mr-3"></i>
<div>
<div class="text-sm font-medium text-gray-900">Foundation Blueprint</div>
<div class="text-sm text-gray-500">Foundation design and specifications</div>
</div>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm text-gray-900">Harar Branch</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="status-badge status-badge-info">Blueprint</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">v2.3</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">Yonas
Mekonnen</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">Feb 10,
2025</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<button class="text-blue-600 hover:text-blue-900 mr-3">
<i class="fas fa-download"></i>
</button>
<button class="text-gray-600 hover:text-gray-900">
<i class="fas fa-ellipsis-v"></i>
</button>
</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center">
<i class="fas fa-file-contract text-blue-500 mr-3"></i>
<div>
<div class="text-sm font-medium text-gray-900">Construction Contract</div>
<div class="text-sm text-gray-500">Main contractor agreement</div>
</div>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm text-gray-900">CBC Headquarters</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="status-badge status-badge-info">Contract</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">v1.0</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">Samuel
Girma</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">Jan 5, 2025</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<button class="text-blue-600 hover:text-blue-900 mr-3">
<i class="fas fa-download"></i>
</button>
<button class="text-gray-600 hover:text-gray-900">
<i class="fas fa-ellipsis-v"></i>
</button>
</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center">
<i class="fas fa-file-alt text-green-500 mr-3"></i>
<div>
<div class="text-sm font-medium text-gray-900">Environmental Impact
Report</div>
<div class="text-sm text-gray-500">Environmental assessment results</div>
</div>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm text-gray-900">Adama Branch</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="status-badge status-badge-info">Report</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">v1.2</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">Hiwot
Tadesse</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">Feb 3, 2025</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<button class="text-blue-600 hover:text-blue-900 mr-3">
<i class="fas fa-download"></i>
</button>
<button class="text-gray-600 hover:text-gray-900">
<i class="fas fa-ellipsis-v"></i>
</button>
</td>
</tr>
</tbody>
</table>
</div>
<!-- Upload Document Modal (Hidden by default) -->
<div id="upload-doc-modal" class="fixed inset-0 z-50 hidden flex items-center justify-
center bg-black bg-opacity-50">
<div class="bg-white rounded-lg shadow-xl w-full max-w-2xl">
<div class="flex justify-between items-center border-b p-4">
<h3 class="text-lg font-semibold">Upload New Document</h3>
<button id="close-upload-modal" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-times"></i>
</button>
</div>
<div class="p-6">
<div class="mb-4">
<label class="block text-gray-700 text-sm font-medium mb-2">Project</label>
<select class="custom-select w-full px-4 py-2 border rounded-lg">
<option value="">Select Project</option>
<option value="harar">Harar Branch</option>
<option value="adama">Adama Branch</option>
<option value="cbc">CBC Headquarters</option>
</select>
</div>
<div class="mb-4">
<label class="block text-gray-700 text-sm font-medium mb-2">Document
Type</label>
<select class="custom-select w-full px-4 py-2 border rounded-lg">
<option value="">Select Type</option>
<option value="blueprint">Blueprint</option>
<option value="contract">Contract</option>
<option value="report">Report</option>
<option value="approval">Approval</option>
</select>
</div>
<div class="mb-4">
<label class="block text-gray-700 text-sm font-medium mb-2">Document
Name</label>
<input type="text" class="w-full px-4 py-2 border rounded-lg" placeholder="Enter
document title">
</div>
<div class="mb-4">
<label class="block text-gray-700 text-sm font-medium mb-
2">Description</label>
<textarea class="w-full px-4 py-2 border rounded-lg" rows="3" placeholder="Enter
document description"></textarea>
</div>
<div class="mb-6">
<label class="block text-gray-700 text-sm font-medium mb-2">File</label>
<div class="file-drop-area">
<i class="fas fa-cloud-upload-alt text-4xl text-gray-400 mb-3"></i>
<p class="text-gray-600">Drag & drop files here or click to browse</p>
<p class="text-xs text-gray-500 mt-1">Supported formats: PDF, DWG, CAD, DOC,
XLS (Max 50MB)</p>
<input type="file" class="hidden">
</div>
</div>
<div class="flex justify-end">
<button class="px-4 py-2 bg-gray-200 text-gray-800 rounded mr-
2">Cancel</button>
<button class="px-4 py-2 bg-blue-600 text-white rounded">Upload
Document</button>
</div>
</div>
</div>
</div>
</div>
<!-- Issue Reporting Module -->
<div id="issues-module" class="module">
<div class="flex flex-col md:flex-row gap-6">
<!-- Report Form -->
<div class="w-full md:w-1/2">
<div class="bg-white rounded-lg shadow-sm p-6">
<h2 class="text-xl font-semibold text-gray-800 mb-4">Report New Issue</h2>
<form>
<div class="mb-4">
<label class="block text-gray-700 text-sm font-medium mb-2">Project
Site</label>
<select class="custom-select w-full px-4 py-2 border rounded-lg">
<option value="">Select Project Site</option>
<option value="harar">Harar Branch</option>
<option value="adama">Adama Branch</option>
<option value="cbc">CBC Headquarters</option>
</select>
</div>
<div class="mb-4">
<label class="block text-gray-700 text-sm font-medium mb-2">Issue
Type</label>
<select class="custom-select w-full px-4 py-2 border rounded-lg">
<option value="">Select Issue Type</option>
<option value="safety">Safety Hazard</option>
<option value="material">Material Shortage</option>
<option value="quality">Quality Concern</option>
<option value="delay">Schedule Delay</option>
<option value="other">Other</option>
</select>
</div>
<div class="mb-4">
<label class="block text-gray-700 text-sm font-medium mb-2">Priority</label>
<div class="flex space-x-4">
<label class="inline-flex items-center">
<input type="radio" name="priority" value="low" class="form-radio text-blue-
600">
<span class="ml-2 text-gray-700">Low</span>
</label>
<label class="inline-flex items-center">
<input type="radio" name="priority" value="medium" class="form-radio text-
yellow-500">
<span class="ml-2 text-gray-700">Medium</span>
</label>
<label class="inline-flex items-center">
<input type="radio" name="priority" value="high" class="form-radio text-red-
600">
<span class="ml-2 text-gray-700">High</span>
</label>
</div>
</div>
<div class="mb-4">
<label class="block text-gray-700 text-sm font-medium mb-2">Issue
Description</label>
<textarea class="w-full px-4 py-2 border rounded-lg" rows="4"
placeholder="Describe the issue in detail..."></textarea>
</div>
<div class="mb-6">
<label class="block text-gray-700 text-sm font-medium mb-2">Attach Photos (up
to 3)</label>
<div class="grid grid-cols-3 gap-4">
<div class="file-drop-area h-32 flex flex-col items-center justify-center">
<i class="fas fa-camera text-2xl text-gray-400"></i>
<p class="text-xs text-gray-500 mt-1">Add Photo</p>
<input type="file" accept="image/*" class="hidden">
</div>
<div class="file-drop-area h-32 flex flex-col items-center justify-center">
<i class="fas fa-camera text-2xl text-gray-400"></i>
<p class="text-xs text-gray-500 mt-1">Add Photo</p>
<input type="file" accept="image/*" class="hidden">
</div>
<div class="file-drop-area h-32 flex flex-col items-center justify-center">
<i class="fas fa-camera text-2xl text-gray-400"></i>
<p class="text-xs text-gray-500 mt-1">Add Photo</p>
<input type="file" accept="image/*" class="hidden">
</div>
</div>
</div>
<div class="flex justify-end">
<button type="submit" class="px-6 py-2 bg-blue-600 text-white rounded-lg
shadow-sm">Submit Report</button>
</div>
</form>
</div>
</div>
<!-- Issues Dashboard -->
<div class="w-full md:w-1/2">
<div class="bg-white rounded-lg shadow-sm p-6">
<h2 class="text-xl font-semibold text-gray-800 mb-4">Recent Issues</h2>
<div class="flex mb-4">
<button class="px-4 py-1 bg-blue-600 text-white rounded-l-lg">All</button>
<button class="px-4 py-1 bg-white text-gray-700 border-t border-b border-
r">Pending</button>
<button class="px-4 py-1 bg-white text-gray-700 border-t border-b border-r
rounded-r-lg">Resolved</button>
</div>
<div class="space-y-4 max-h-[600px] overflow-y-auto">
<!-- Issue Card -->
<div class="border rounded-lg p-4">
<div class="flex justify-between items-start">
<div>
<h3 class="font-medium text-gray-800">Material Shortage - Steel Rebar</h3>
<p class="text-sm text-gray-600">CBC Headquarters</p>
</div>
<span class="status-badge status-badge-warning">Pending</span>
</div>
<p class="mt-2 text-gray-600 text-sm">Not enough steel rebars delivered for
foundation work. Need additional 2 tons to continue construction.</p>
<div class="mt-2 flex items-center text-xs text-gray-500">
<span>Reported by: Dawit Haile</span>
<span class="mx-2">•</span>
<span>Feb 12, 2025</span>
</div>
<div class="mt-3 flex justify-end">
<button class="px-3 py-1 bg-green-100 text-green-800 text-xs rounded-lg">Mark
as Resolved</button>
</div>
</div>
<!-- Issue Card -->
<div class="border rounded-lg p-4">
<div class="flex justify-between items-start">
<div>
<h3 class="font-medium text-gray-800">Safety Hazard - Unsecured
Scaffolding</h3>
<p class="text-sm text-gray-600">Harar Branch</p>
</div>
<span class="status-badge status-badge-danger">High Priority</span>
</div>
<p class="mt-2 text-gray-600 text-sm">Scaffolding on the east side of the building
is not properly secured. Risk of collapse.</p>
<div class="mt-2 flex flex-wrap gap-2">
<div class="w-16 h-16 bg-gray-200 rounded flex items-center justify-center">
<i class="fas fa-image text-gray-400"></i>
</div>
<div class="w-16 h-16 bg-gray-200 rounded flex items-center justify-center">
<i class="fas fa-image text-gray-400"></i>
</div>
</div>
<div class="mt-2 flex items-center text-xs text-gray-500">
<span>Reported by: Selam Berhanu</span>
<span class="mx-2">•</span>
<span>Feb 14, 2025</span>
</div>
<div class="mt-3 flex justify-end">
<button class="px-3 py-1 bg-green-100 text-green-800 text-xs rounded-lg">Mark
as Resolved</button>
</div>
</div>
<!-- Issue Card (Resolved) -->
<div class="border rounded-lg p-4 bg-gray-50">
<div class="flex justify-between items-start">
<div>
<h3 class="font-medium text-gray-800">Schedule Delay - Electrical Work</h3>
<p class="text-sm text-gray-600">Adama Branch</p>
</div>
<span class="status-badge status-badge-success">Resolved</span>
</div>
<p class="mt-2 text-gray-600 text-sm">Electricians delayed due to equipment
delivery issues. Schedule adjusted.</p>
<div class="mt-2 flex items-center text-xs text-gray-500">
<span>Resolved by: Tigist Alemu</span>
<span class="mx-2">•</span>
<span>Feb 10, 2025</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- GIS Map Module -->
<div id="map-module" class="module">
<div class="flex flex-col lg:flex-row gap-6">
<!-- Map Container -->
<div class="w-full lg:w-3/4">
<div class="bg-white rounded-lg shadow-sm p-4">
<div id="project-map" class="map-container rounded-lg"></div>
</div>
</div>
<!-- Site List -->
<div class="w-full lg:w-1/4">
<div class="bg-white rounded-lg shadow-sm p-4">
<h3 class="text-lg font-semibold text-gray-800 mb-4">Project Sites</h3>
<div class="mb-4">
<div class="relative">
<input type="text" placeholder="Search sites..." class="w-full px-4 py-2 border
rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
<i class="fas fa-search absolute right-3 top-3 text-gray-400"></i>
</div>
</div>
<div class="space-y-3 max-h-[500px] overflow-y-auto">
<div class="p-3 border rounded-lg cursor-pointer hover:bg-blue-50 bg-blue-50">
<h4 class="font-medium text-blue-700">Harar Branch</h4>
<div class="flex justify-between mt-1 text-sm">
<span class="text-gray-600">Progress:</span>
<span class="text-gray-800 font-medium">65%</span>
</div>
<div class="mt-2 text-xs text-gray-500">
<div><span class="font-medium">Manager:</span> Abebe Kebede</div>
<div><span class="font-medium">Contractor:</span> East Construction
Ltd.</div>
</div>
</div>
<div class="p-3 border rounded-lg cursor-pointer hover:bg-blue-50">
<h4 class="font-medium text-gray-800">Adama Branch</h4>
<div class="flex justify-between mt-1 text-sm">
<span class="text-gray-600">Progress:</span>
<span class="text-gray-800 font-medium">82%</span>
</div>
<div class="mt-2 text-xs text-gray-500">
<div><span class="font-medium">Manager:</span> Tigist Alemu</div>
<div><span class="font-medium">Contractor:</span> Modern Builders
Inc.</div>
</div>
</div>
<div class="p-3 border rounded-lg cursor-pointer hover:bg-blue-50">
<h4 class="font-medium text-gray-800">CBC Headquarters</h4>
<div class="flex justify-between mt-1 text-sm">
<span class="text-gray-600">Progress:</span>
<span class="text-gray-800 font-medium">35%</span>
</div>
<div class="mt-2 text-xs text-gray-500">
<div><span class="font-medium">Manager:</span> Solomon Teshome</div>
<div><span class="font-medium">Contractor:</span> Addis Construction
Group</div>
</div>
</div>
<div class="p-3 border rounded-lg cursor-pointer hover:bg-blue-50">
<h4 class="font-medium text-gray-800">Bahir Dar Branch</h4>
<div class="flex justify-between mt-1 text-sm">
<span class="text-gray-600">Progress:</span>
<span class="text-gray-800 font-medium">10%</span>
</div>
<div class="mt-2 text-xs text-gray-500">
<div><span class="font-medium">Manager:</span> Ezana Tesfaye</div>
<div><span class="font-medium">Contractor:</span> Lake Tana Builders</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Resource & Cost Module -->
<div id="resources-module" class="module">
<!-- Budget & Cost Overview -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
<div class="bg-white rounded-lg shadow-sm p-6">
<h3 class="text-lg font-semibold text-gray-800 mb-4">Budget vs. Actual Cost</h3>
<div class="h-64">
<canvas id="budget-chart"></canvas>
</div>
<div class="mt-4 flex justify-end">
<button class="px-3 py-1.5 text-sm bg-white border border-gray-300 text-gray-700
rounded flex items-center">
<i class="fas fa-download mr-2"></i> Export Report
</button>
</div>
</div>
<div class="bg-white rounded-lg shadow-sm p-6">
<h3 class="text-lg font-semibold text-gray-800 mb-4">Material Inventory</h3>
<div class="h-64">
<canvas id="inventory-chart"></canvas>
</div>
<div class="mt-4 flex justify-end">
<button class="px-3 py-1.5 text-sm bg-blue-600 text-white rounded">
Manage Inventory
</button>
</div>
</div>
</div>
<!-- Resource Tables -->
<div class="bg-white rounded-lg shadow-sm overflow-hidden">
<div class="p-4 border-b border-gray-200">
<h3 class="text-lg font-semibold text-gray-800">Materials & Resources</h3>
</div>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500
uppercase tracking-wider">Material</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500
uppercase tracking-wider">Project</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500
uppercase tracking-wider">Stock Level</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500
uppercase tracking-wider">Budget</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500
uppercase tracking-wider">Actual Cost</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500
uppercase tracking-wider">Status</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
<tr>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm font-medium text-gray-900">Cement (50kg bags)</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">Harar
Branch</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm text-gray-900">2,500 / 3,000</div>
<div class="progress-container mt-1 w-32">
<div class="progress-bar progress-bar-success" style="width: 83%"></div>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">ETB
450,000</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">ETB
420,000</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="status-badge status-badge-success">On Budget</span>
</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm font-medium text-gray-900">Steel Rebar (tons)</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">CBC
Headquarters</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm text-gray-900">45 / 120</div>
<div class="progress-container mt-1 w-32">
<div class="progress-bar progress-bar-warning" style="width: 37%"></div>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">ETB
1,800,000</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">ETB
1,950,000</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="status-badge status-badge-danger">Over Budget</span>
</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm font-medium text-gray-900">Glass Panels (sq.
meters)</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">Adama
Branch</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm text-gray-900">320 / 320</div>
<div class="progress-container mt-1 w-32">
<div class="progress-bar progress-bar-success" style="width: 100%"></div>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">ETB
640,000</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">ETB
635,000</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="status-badge status-badge-success">Complete</span>
</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm font-medium text-gray-900">Electrical Wiring (meters)</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">CBC
Headquarters</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm text-gray-900">2,100 / 5,500</div>
<div class="progress-container mt-1 w-32">
<div class="progress-bar progress-bar-warning" style="width: 38%"></div>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">ETB
275,000</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">ETB
230,000</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="status-badge status-badge-success">On Budget</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- Design Review Module -->
<div id="design-module" class="module">
<div class="flex justify-between items-center mb-6">
<div class="w-1/2">
<div class="relative">
<input type="text" placeholder="Search design documents..." class="w-full px-4 py-
2 border rounded-lg">
<i class="fas fa-search absolute right-3 top-3 text-gray-400"></i>
</div>
</div>
<div class="flex space-x-2">
<select class="custom-select px-4 py-2 border rounded-lg">
<option value="">All Projects</option>
<option value="harar">Harar Branch</option>
<option value="adama">Adama Branch</option>
<option value="cbc">CBC Headquarters</option>
</select>
<select class="custom-select px-4 py-2 border rounded-lg">
<option value="">All Statuses</option>
<option value="pending">Pending</option>
<option value="review">Under Review</option>
<option value="approved">Approved</option>
<option value="rejected">Rejected</option>
</select>
<button class="px-4 py-2 bg-blue-600 text-white rounded-lg shadow-sm flex items-
center">
<i class="fas fa-upload mr-2"></i> Upload Design
</button>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<!-- Design Card -->
<div class="card p-0 overflow-hidden">
<div class="h-48 bg-gray-200 flex items-center justify-center relative">
<i class="fas fa-image text-gray-400 text-4xl"></i>
<span class="absolute top-2 right-2 status-badge status-badge-warning">Under
Review</span>
</div>
<div class="p-4">
<h3 class="text-lg font-semibold text-gray-800">Facade Design - Main
Entrance</h3>
<p class="text-sm text-gray-600 mt-1">CBC Headquarters</p>
<div class="flex items-center mt-3 text-sm">
<i class="fas fa-clock text-gray-400 mr-1"></i>
<span class="text-gray-600">Submitted: Feb 12, 2025</span>
</div>
<div class="flex items-center mt-1 text-sm">
<i class="fas fa-user text-gray-400 mr-1"></i>
<span class="text-gray-600">Designer: Yohannes Abate</span>
</div>
<div class="mt-4 flex justify-between">
<button class="px-3 py-1.5 text-sm bg-blue-600 text-white rounded">Review &
Annotate</button>
<div class="flex space-x-2">
<button class="w-8 h-8 bg-gray-100 rounded-full flex items-center justify-center
text-gray-600">
<i class="fas fa-history"></i>
</button>
<button class="w-8 h-8 bg-gray-100 rounded-full flex items-center justify-center
text-gray-600">
<i class="fas fa-download"></i>
</button>
</div>
</div>
</div>
</div>
<!-- Design Card -->
<div class="card p-0 overflow-hidden">
<div class="h-48 bg-gray-200 flex items-center justify-center relative">
<i class="fas fa-image text-gray-400 text-4xl"></i>
<span class="absolute top-2 right-2 status-badge status-badge-
danger">Rejected</span>
</div>
<div class="p-4">
<h3 class="text-lg font-semibold text-gray-800">Floor Plan - Level 3</h3>
<p class="text-sm text-gray-600 mt-1">Harar Branch</p>
<div class="flex items-center mt-3 text-sm">
<i class="fas fa-clock text-gray-400 mr-1"></i>
<span class="text-gray-600">Submitted: Feb 8, 2025</span>
</div>
<div class="flex items-center mt-1 text-sm">
<i class="fas fa-user text-gray-400 mr-1"></i>
<span class="text-gray-600">Designer: Helen Mengesha</span>
</div>
<div class="mt-4 flex justify-between">
<button class="px-3 py-1.5 text-sm bg-blue-600 text-white rounded">View
Feedback</button>
<div class="flex space-x-2">
<button class="w-8 h-8 bg-gray-100 rounded-full flex items-center justify-center
text-gray-600">
<i class="fas fa-history"></i>
</button>
<button class="w-8 h-8 bg-gray-100 rounded-full flex items-center justify-center
text-gray-600">
<i class="fas fa-download"></i>
</button>
</div>
</div>
</div>
</div>
<!-- Design Card -->
<div class="card p-0 overflow-hidden">
<div class="h-48 bg-gray-200 flex items-center justify-center relative">
<i class="fas fa-image text-gray-400 text-4xl"></i>
<span class="absolute top-2 right-2 status-badge status-badge-
success">Approved</span>
</div>
<div class="p-4">
<h3 class="text-lg font-semibold text-gray-800">Electrical Layout - Ground
Floor</h3>
<p class="text-sm text-gray-600 mt-1">Adama Branch</p>
<div class="flex items-center mt-3 text-sm">
<i class="fas fa-clock text-gray-400 mr-1"></i>
<span class="text-gray-600">Approved: Feb 5, 2025</span>
</div>
<div class="flex items-center mt-1 text-sm">
<i class="fas fa-user text-gray-400 mr-1"></i>
<span class="text-gray-600">Designer: Belay Tadesse</span>
</div>
<div class="mt-4 flex justify-between">
<button class="px-3 py-1.5 text-sm bg-gray-200 text-gray-700 rounded">View
Design</button>
<div class="flex space-x-2">
<button class="w-8 h-8 bg-gray-100 rounded-full flex items-center justify-center
text-gray-600">
<i class="fas fa-history"></i>
</button>
<button class="w-8 h-8 bg-gray-100 rounded-full flex items-center justify-center
text-gray-600">
<i class="fas fa-download"></i>
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Design Review Modal (Hidden by default) -->
<div id="design-review-modal" class="fixed inset-0 z-50 hidden flex items-center
justify-center bg-black bg-opacity-50">
<div class="bg-white rounded-lg shadow-xl w-full max-w-5xl h-3/4">
<div class="flex justify-between items-center border-b p-4">
<h3 class="text-lg font-semibold">Review: Facade Design - Main Entrance</h3>
<button id="close-review-modal" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-times"></i>
</button>
</div>
<div class="flex h-full">
<div class="w-3/4 p-4 border-r">
<div class="bg-gray-200 h-full flex items-center justify-center">
<canvas id="annotation-canvas" class="annotation-canvas w-full h-
full"></canvas>
</div>
</div>
<div class="w-1/4 p-4 flex flex-col h-full">
<div class="mb-4">
<h4 class="font-medium text-gray-800 mb-2">Review Status</h4>
<select class="custom-select w-full px-4 py-2 border rounded-lg">
<option value="review">Under Review</option>
<option value="approved">Approved</option>
<option value="rejected">Rejected</option>
<option value="revision">Needs Revision</option>
</select>
</div>
<div class="mb-4 flex-grow">
<h4 class="font-medium text-gray-800 mb-2">Comments</h4>
<textarea class="w-full h-40 px-4 py-2 border rounded-lg resize-none"
placeholder="Add your comments or feedback..."></textarea>
</div>
<div class="mb-4">
<h4 class="font-medium text-gray-800 mb-2">Previous Revisions</h4>
<div class="text-sm text-gray-600">
<div class="flex items-center mb-2">
<i class="fas fa-history text-gray-400 mr-2"></i>
<span>Version 1.2 - Feb 10, 2025</span>
</div>
<div class="flex items-center">
<i class="fas fa-history text-gray-400 mr-2"></i>
<span>Version 1.1 - Feb 5, 2025</span>
</div>
</div>
</div>
<div class="mt-auto flex justify-end space-x-2">
<button class="px-4 py-2 bg-gray-200 text-gray-800 rounded">Cancel</button>
<button class="px-4 py-2 bg-blue-600 text-white rounded">Submit
Review</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script id="app-script">
// Module navigation
[Link]('DOMContentLoaded', function() {
const navItems = [Link]('.nav-item');
const modules = [Link]('.module');
const moduleTitle = [Link]('module-title');
const moduleDescription = [Link]('module-description');
const moduleTitles = {
'dashboard': 'Project Dashboard',
'documents': 'Document Management',
'issues': 'Site Issue Reporting',
'map': 'Interactive GIS Map',
'resources': 'Resource & Cost Monitoring',
'design': 'Design Review & Approval'
};
const moduleDescriptions = {
'dashboard': 'Track and manage all ongoing construction projects',
'documents': 'Centralize all technical drawings, contracts and approvals',
'issues': 'Report and track construction site issues',
'map': 'View all project sites on an interactive map',
'resources': 'Monitor project resources, materials and costs',
'design': 'Review and approve design documents'
};
[Link](item => {
[Link]('click', function() {
const moduleId = [Link];
// Update active nav item
[Link](nav => [Link]('active'));
[Link]('active');
// Show active module
[Link](module => {
if ([Link] === `${moduleId}-module`) {
[Link]('active');
} else {
[Link]('active');
}
});
// Update header
[Link] = moduleTitles[moduleId];
[Link] = moduleDescriptions[moduleId];
// Initialize module specific functionality
if (moduleId === 'map' && !mapInitialized) {
initializeMap();
} else if (moduleId === 'resources' && !chartsInitialized) {
initializeCharts();
}
});
});
// Toggle sidebar collapse
const toggleSidebar = [Link]('toggle-sidebar');
const sidebar = [Link]('sidebar');
const sidebarTexts = [Link]('.sidebar-text');
[Link]('click', function() {
[Link]('sidebar-collapsed');
[Link](text => {
[Link]('hidden');
});
});
// Dashboard view toggle
const cardViewBtn = [Link]('card-view-btn');
const ganttViewBtn = [Link]('gantt-view-btn');
const cardView = [Link]('card-view');
const ganttView = [Link]('gantt-view');
[Link]('click', function() {
[Link]('hidden');
[Link]('hidden');
[Link]('bg-blue-600', 'text-white');
[Link]('bg-white', 'text-gray-700');
[Link]('bg-white', 'text-gray-700');
[Link]('bg-blue-600', 'text-white');
});
[Link]('click', function() {
[Link]('hidden');
[Link]('hidden');
[Link]('bg-blue-600', 'text-white');
[Link]('bg-white', 'text-gray-700');
[Link]('bg-white', 'text-gray-700');
[Link]('bg-blue-600', 'text-white');
});
// Document Upload Modal
const uploadDocBtn = [Link]('upload-doc-btn');
const uploadDocModal = [Link]('upload-doc-modal');
const closeUploadModal = [Link]('close-upload-modal');
if (uploadDocBtn && uploadDocModal && closeUploadModal) {
[Link]('click', function() {
[Link]('hidden');
});
[Link]('click', function() {
[Link]('hidden');
});
}
// Design Review Modal
const designReviewBtns = [Link]('.card button');
const designReviewModal = [Link]('design-review-modal');
const closeReviewModal = [Link]('close-review-modal');
if (designReviewModal && closeReviewModal) {
[Link](btn => {
if ([Link]('Review')) {
[Link]('click', function() {
[Link]('hidden');
});
}
});
[Link]('click', function() {
[Link]('hidden');
});
}
// New Project Button
const addProjectBtn = [Link]('add-project-btn');
if (addProjectBtn) {
[Link]('click', function() {
[Link]({
title: 'Create New Project',
html: `
<div class="mb-4">
<label class="block text-gray-700 text-sm font-medium mb-2 text-left">Project
Name</label>
<input id="project-name" class="w-full px-4 py-2 border rounded-lg">
</div>
<div class="mb-4">
<label class="block text-gray-700 text-sm font-medium mb-2 text-
left">Location</label>
<input id="project-location" class="w-full px-4 py-2 border rounded-lg">
</div>
<div class="mb-4">
<label class="block text-gray-700 text-sm font-medium mb-2 text-left">Project
Manager</label>
<select id="project-manager" class="w-full px-4 py-2 border rounded-lg">
<option value="">Select Manager</option>
<option value="1">Abebe Kebede</option>
<option value="2">Tigist Alemu</option>
<option value="3">Solomon Teshome</option>
</select>
</div>
<div class="grid grid-cols-2 gap-4 mb-4">
<div>
<label class="block text-gray-700 text-sm font-medium mb-2 text-left">Start
Date</label>
<input type="date" id="start-date" class="w-full px-4 py-2 border rounded-lg">
</div>
<div>
<label class="block text-gray-700 text-sm font-medium mb-2 text-left">End
Date</label>
<input type="date" id="end-date" class="w-full px-4 py-2 border rounded-lg">
</div>
</div>
`,
showCancelButton: true,
confirmButtonText: 'Create Project',
confirmButtonColor: '#2563eb',
cancelButtonText: 'Cancel',
focusConfirm: false,
preConfirm: () => {
return {
name: [Link]('project-name').value,
location: [Link]('project-location').value,
manager: [Link]('project-manager').value,
startDate: [Link]('start-date').value,
endDate: [Link]('end-date').value
};
}
}).then((result) => {
if ([Link]) {
[Link](
'Project Created!',
'The new project has been added successfully.',
'success'
);
}
});
});
}
// File upload areas
const fileDropAreas = [Link]('.file-drop-area');
[Link](area => {
const input = [Link]('input[type="file"]');
// Click to browse
[Link]('click', () => {
[Link]();
});
// Display selected file
[Link]('change', function() {
if ([Link] && [Link][0]) {
[Link]('border-blue-500');
// If this is an image input in the issue reporting form
if ([Link] && [Link]('image')) {
const icon = [Link]('i');
if (icon) [Link] = 'fas fa-check-circle text-2xl text-green-500';
const text = [Link]('p');
if (text) [Link] = [Link][0].name;
}
}
});
// Drag and drop functionality
[Link]('dragover', (e) => {
[Link]();
[Link]('active');
});
[Link]('dragleave', () => {
[Link]('active');
});
[Link]('drop', (e) => {
[Link]();
[Link]('active');
if ([Link]) {
[Link] = [Link];
// Trigger change event
const event = new Event('change', { bubbles: true });
[Link](event);
}
});
});
});
// GIS Map
let mapInitialized = false;
function initializeMap() {
if (!mapInitialized && [Link]('project-map')) {
// Initialize map centered on Ethiopia
const map = [Link]('project-map').setView([9.0, 38.7], 6);
// Add OpenStreetMap tiles
[Link]('[Link] {
attribution: '© <a
href="[Link] contributors'
}).addTo(map);
// Add markers for project sites
const sites = [
{
name: "Harar Branch",
location: [9.31, 42.12],
progress: 65,
manager: "Abebe Kebede",
contractor: "East Construction Ltd.",
status: "At Risk"
},
{
name: "Adama Branch",
location: [8.54, 39.27],
progress: 82,
manager: "Tigist Alemu",
contractor: "Modern Builders Inc.",
status: "On Track"
},
{
name: "CBC Headquarters",
location: [9.02, 38.75],
progress: 35,
manager: "Solomon Teshome",
contractor: "Addis Construction Group",
status: "Delayed"
},
{
name: "Bahir Dar Branch",
location: [11.6, 37.4],
progress: 10,
manager: "Ezana Tesfaye",
contractor: "Lake Tana Builders",
status: "On Track"
}
];
// Add markers with popups
[Link](site => {
const marker = [Link]([Link]).addTo(map);
// Determine status color
let statusColor = "green";
if ([Link] === "At Risk") statusColor = "orange";
if ([Link] === "Delayed") statusColor = "red";
const popupContent = `
<div class="p-2">
<h3 class="font-bold">${[Link]}</h3>
<p class="my-1">Progress: ${[Link]}%</p>
<div class="progress-container my-1" style="height:6px;">
<div class="progress-bar" style="width:${[Link]}%; background-
color:${statusColor};"></div>
</div>
<p class="text-sm"><strong>Status:</strong> ${[Link]}</p>
<p class="text-sm"><strong>Manager:</strong> ${[Link]}</p>
<p class="text-sm"><strong>Contractor:</strong> ${[Link]}</p>
<a href="javascript:void(0)" class="text-blue-600 text-sm">View Details</a>
</div>
`;
[Link](popupContent);
// Connect site list to map
const siteItem = [Link](`.[Link]:contains('${[Link]}')`);
if (siteItem) {
[Link]('click', function() {
[Link]([Link], 12);
[Link]();
});
}
});
mapInitialized = true;
}
}
// Resource charts
let chartsInitialized = false;
function initializeCharts() {
if (!chartsInitialized) {
// Budget vs. Actual chart
const budgetCtx = [Link]('budget-chart');
if (budgetCtx) {
new Chart(budgetCtx, {
type: 'bar',
data: {
labels: ['Harar Branch', 'Adama Branch', 'CBC Headquarters'],
datasets: [
{
label: 'Budget (Million ETB)',
data: [45, 28, 120],
backgroundColor: 'rgba(59, 130, 246, 0.6)',
borderColor: 'rgb(59, 130, 246)',
borderWidth: 1
},
{
label: 'Actual Cost (Million ETB)',
data: [40, 25, 52],
backgroundColor: 'rgba(16, 185, 129, 0.6)',
borderColor: 'rgb(16, 185, 129)',
borderWidth: 1
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true,
title: {
display: true,
text: 'Million ETB'
}
}
}
}
});
}
// Material inventory chart
const inventoryCtx = [Link]('inventory-chart');
if (inventoryCtx) {
new Chart(inventoryCtx, {
type: 'pie',
data: {
labels: ['Cement', 'Steel', 'Wood', 'Glass', 'Electrical', 'Plumbing'],
datasets: [{
data: [30, 25, 15, 8, 12, 10],
backgroundColor: [
'rgba(59, 130, 246, 0.6)',
'rgba(16, 185, 129, 0.6)',
'rgba(245, 158, 11, 0.6)',
'rgba(239, 68, 68, 0.6)',
'rgba(168, 85, 247, 0.6)',
'rgba(236, 72, 153, 0.6)'
],
borderColor: [
'rgb(59, 130, 246)',
'rgb(16, 185, 129)',
'rgb(245, 158, 11)',
'rgb(239, 68, 68)',
'rgb(168, 85, 247)',
'rgb(236, 72, 153)'
],
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'right'
}
}
}
});
}
chartsInitialized = true;
}
}
// Helper for site list selection
[Link][':'].contains = function(a, i, m) {
return jQuery(a).text().toUpperCase().indexOf(m[3].toUpperCase()) >= 0;
};
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Construction Dashboard - Coopbank (Full Feature)</title>
<!-- Charts -->
<script src="[Link]
<!-- Excel export -->
<script src="[Link]
<!-- PDF export -->
<script src="[Link]
<script src="[Link]
autotable@3.8.2/dist/[Link]"></script>
<!-- Firebase (optional; only used if USE_FIREBASE=true & you add config below) -->
<script type="module" id="firebase-mod">
// Toggle this flag to true after you paste your config below
const USE_FIREBASE = false; // <-- set to true when ready
// Paste your Firebase config here when ready
const firebaseConfig = {
// apiKey: "...",
// authDomain: "...",
// projectId: "...",
// storageBucket: "...",
// messagingSenderId: "...",
// appId: "..."
};
if (USE_FIREBASE) {
import('[Link] ({
initializeApp }) => {
const { getFirestore, doc, getDoc, setDoc, updateDoc, collection, addDoc, getDocs,
deleteDoc } = await import('[Link]
[Link]');
const { getStorage, ref, uploadBytes, getDownloadURL } = await
import('[Link]
const { getAuth, signInWithEmailAndPassword } = await
import('[Link]
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);
const storage = getStorage(app);
const auth = getAuth(app);
// expose minimal firebase API to window for use in the main script
window.__FB__ = { USE_FIREBASE, db, storage, auth, addDoc, collection, getDocs, doc,
getDoc, setDoc, updateDoc, deleteDoc, ref, uploadBytes, getDownloadURL,
signInWithEmailAndPassword };
});
} else {
window.__FB__ = { USE_FIREBASE: false };
}
</script>
<style>
:root {
--brand: #005f3c;
--brand-dark: #00452d;
--bg: #f4f6f9;
--card: #ffffff;
--text: #1f2937;
--muted: #6b7280;
--border: #e5e7eb;
--warning: #f59e0b;
--info: #3b82f6;
--danger: #ef4444;
--success: #10b981;
}
* { box-sizing: border-box; }
body { margin:0; font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-
serif; background: var(--bg); color: var(--text); }
header { background: var(--brand); color: #fff; text-align: center; padding: 16px; }
header h1 { margin: 0 0 4px; }
.login { width: 380px; max-width: 92vw; margin: 64px auto; background: var(--card);
border-radius: 14px; padding: 18px; box-shadow: 0 10px 24px rgba(0,0,0,.08); }
input, select, button, textarea { width: 100%; padding: 10px 12px; margin-top: 10px;
border-radius: 10px; border: 1px solid var(--border); font-size: 14px; }
button { background: var(--brand); color: #fff; border: none; cursor: pointer; font-weight:
700; }
button:hover { filter: brightness(.95); }
[Link] { background: #1f2937; }
[Link] { background: #fff; color: var(--brand); border: 1px solid var(--brand); }
.app { display: none; max-width: 1440px; margin: 0 auto; padding: 18px; }
.grid { display: grid; gap: 18px; grid-template-columns: repeat(12, 1fr); }
.card { background: var(--card); border-radius: 16px; padding: 18px; box-shadow: 0 6px
18px rgba(0,0,0,.06); }
.hint { font-size: 12px; color: var(--muted); }
.row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.kpi { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap:
12px; }
.kpi .item { border: 1px dashed var(--border); padding: 12px; border-radius: 12px; }
.kpi .label { font-size: 12px; color: var(--muted); }
.kpi .value { font-weight: 800; }
.progress-bar { background: #e5e7eb; border-radius: 999px; overflow: hidden; height:
18px; }
.progress { height: 100%; border-radius: 999px; font-size: 12px; line-height: 18px; color:
#fff; text-align: right; padding-right: 6px; }
/* Table */
.table-wrap { overflow:auto; border: 1px solid var(--border); border-radius: 12px; }
table { width: 100%; border-collapse: collapse; font-size: 14px; }
th, td { padding: 10px; border-bottom: 1px solid var(--border); text-align: left; }
th { position: sticky; top: 0; background: var(--brand); color: #fff; z-index: 1; }
[Link] { white-space: nowrap; }
/* Gallery */
.gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap:
10px; }
.thumb { position: relative; border: 1px solid var(--border); border-radius: 10px; overflow:
hidden; background: #fafafa; }
.thumb img, .thumb video { width: 100%; height: 120px; object-fit: cover; display: block; }
.thumb .cap { position: absolute; left: 6px; right: 6px; bottom: 6px; background:
rgba(0,0,0,.5); color:#fff; font-size: 11px; padding: 2px 6px; border-radius: 6px; }
/* Gantt */
.gantt { background: var(--card); border-radius: 16px; padding: 18px; box-shadow: 0 6px
18px rgba(0,0,0,.06); grid-column: span 12; }
.gantt-legend { display:flex; gap:12px; font-size:12px; color:var(--muted); margin-
bottom:8px; }
.legend-swatch { width:12px; height:12px; border-radius:3px; display:inline-block; }
.gantt-body { display:grid; grid-template-columns: 240px 1fr; min-height: 260px; border:
1px solid var(--border); border-radius: 12px; overflow: hidden; }
.g-left { border-right: 1px solid var(--border); }
.g-left .lrow { height: 36px; padding: 0 10px; border-bottom: 1px dashed var(--border);
display: flex; align-items: center; font-size: 13px; }
.g-right { position: relative; overflow: auto; }
.days { display: grid; height: 30px; border-bottom: 1px solid var(--border); background:
#fafafa; }
.day-cell { border-left: 1px solid #f0f0f0; font-size: 10px; display:flex; align-items:center;
justify-content:center; color:#6b7280; }
.bars { position: relative; }
.bar-row { position: relative; height: 36px; border-bottom: 1px dashed var(--border); }
.bar { position: absolute; top: 6px; height: 24px; border-radius: 6px; display: flex; align-
items: center; justify-content: center; font-size: 12px; color: #fff; white-space: nowrap;
padding: 0 8px; }
.[Link] { background: var(--muted); }
.[Link] { background: var(--info); }
.[Link] { background: var(--success); }
.[Link] { background: var(--danger); }
@media (max-width: 900px) {
.grid { grid-template-columns: 1fr; }
.gantt-body { grid-template-columns: 1fr; }
.g-left { display: none; }
}
</style>
</head>
<body>
<header>
<h1>Cooperative Bank of Oromia</h1>
<p>Building Construction Management & Administration Dashboard</p>
</header>
<!-- Login -->
<div class="login" id="loginBox">
<h3>Login</h3>
<input id="username" placeholder="Enter name" />
<select id="role">
<option value="director">Senior Director</option>
<option value="manager">Manager</option>
<option value="engineer">Engineer</option>
</select>
<button onclick="login()">Login</button>
<p class="hint">Demo: role affects permissions. Firebase is optional.</p>
</div>
<!-- App -->
<div class="app" id="app">
<div class="grid">
<!-- Controls -->
<div class="card" style="grid-column: span 12;">
<div class="row">
<select id="projectSelect" onchange="selectProject([Link])"></select>
<button class="ghost" onclick="exportExcel()">Export Excel</button>
<button class="ghost" onclick="exportPDF()">Export PDF</button>
<span class="hint">Per-project data is saved locally; enable Firebase in the source to
sync.</span>
</div>
</div>
<!-- Contract & Consultant Details -->
<div class="card" style="grid-column: span 8;">
<h3 style="margin:0 0 8px;">Contractual Details</h3>
<div class="kpi" id="kpiBox"></div>
<div class="row" style="margin-top:10px;">
<input id="cAmount" placeholder="Contract Amount (Birr)" />
<input id="cDays" placeholder="Contract Duration (days)" />
<input id="cContractor" placeholder="Contractor" />
<input id="cConsultant" placeholder="Consultant" />
<input id="cStart" type="date" />
<input id="cPlanned" type="date" />
</div>
<div class="row">
<select id="cConsultantCondition">
<option value="Supervision">Supervision</option>
<option value="Design & Supervision">Design & Supervision</option>
<option value="Contract Administration">Contract Administration</option>
</select>
<textarea id="cNotes" placeholder="Additional contractual notes / special
conditions"></textarea>
</div>
<h4>Guarantee / Bond</h4>
<div class="row">
<select id="gType">
<option>Performance Bond</option>
<option>Advance Payment Guarantee</option>
<option>Bid Bond</option>
<option>Retention Guarantee</option>
</select>
<input id="gAmount" placeholder="Guarantee Amount (Birr)" />
<input id="gExpiry" type="date" placeholder="Expiry" />
<select id="gStatus">
<option>Active</option>
<option>Expired</option>
<option>Claimed</option>
<option>Released</option>
</select>
</div>
<div class="row"><button onclick="saveContract()">Save Contract Info</button></div>
</div>
<!-- Progress -->
<div class="card" style="grid-column: span 4;">
<h3 style="margin:0 0 8px;">Overall Progress</h3>
<div class="progress-bar"><div id="projBar" class="progress"
style="width:0%">0%</div></div>
<canvas id="progressChart" style="margin-top:12px;"></canvas>
</div>
<!-- Site Media -->
<div class="card" style="grid-column: span 12;">
<div class="row" style="justify-content: space-between;">
<h3 style="margin:0;">Site Media (Images & Video)</h3>
</div>
<div class="row" style="margin-top:8px;">
<input type="file" id="mediaInput" multiple accept="image/*,video/*" />
<input id="mediaCaption" placeholder="Caption (optional)" />
<button onclick="addMedia()">Add</button>
</div>
<div id="gallery" class="gallery" style="margin-top:12px;"></div>
</div>
<!-- Tasks -->
<div class="card" style="grid-column: span 12;">
<div class="row" style="justify-content: space-between;">
<h3 style="margin:0;">Task Monitoring</h3>
<div class="row" id="taskToolbar" style="display:none;">
<input id="tName" placeholder="Task" />
<input id="tResp" placeholder="Responsible" />
<input id="tStart" type="date" />
<input id="tEnd" type="date" />
<select id="tStatus">
<option>Pending</option>
<option>In Progress</option>
<option>Completed</option>
<option>Delayed</option>
</select>
<button onclick="saveTask()" id="tSave">Add Task</button>
<button onclick="cancelEdit()" id="tCancel" class="secondary"
style="display:none;">Cancel</button>
</div>
</div>
<div class="table-wrap" style="max-height:360px;">
<table>
<thead>
<tr>
<th>Task</th><th>Responsible</th><th>Start</th><th>End</th><th>Status</th><th>Actio
ns</th>
</tr>
</thead>
<tbody id="taskBody"></tbody>
</table>
</div>
</div>
<!-- Gantt -->
<div class="gantt">
<div class="gantt-legend">
<span><span class="legend-swatch" style="background:var(--muted)"></span>
Pending</span>
<span><span class="legend-swatch" style="background:var(--info)"></span> In
Progress</span>
<span><span class="legend-swatch" style="background:var(--success)"></span>
Completed</span>
<span><span class="legend-swatch" style="background:var(--danger)"></span>
Delayed</span>
</div>
<div class="gantt-body">
<div class="g-left" id="gLeft"></div>
<div class="g-right" id="gRight">
<div class="days" id="gDays"></div>
<div class="bars" id="gBars"></div>
</div>
</div>
<p class="hint">Tip: Scroll the timeline horizontally. Tasks auto-update the Gantt &
KPIs.</p>
</div>
</div>
</div>
<script>
// ===== Utilities =====
const DAY = 24*60*60*1000;
const parse = (d)=> new Date(d+"T[Link]");
const clamp = (v,a,b)=> [Link](a, [Link](b,v));
// ===== Demo Projects + defaults =====
const defaultProjects = [
{ id: 'Harar', name: 'Harar Project', contract: { amount:'316,716,729.64', days:1095,
contractor:'Yirgalem Construction plc', consultant:'', start:'', planned:'',
consultantCondition:'Supervision', notes:'', guarantee:{ type:'Performance Bond', amount:'',
expiry:'', status:'Active' } } },
{ id: 'Adama', name: 'Adama District Office', contract: { amount:'', days:'', contractor:'',
consultant:'', start:'', planned:'', consultantCondition:'Supervision', notes:'', guarantee:{
type:'Performance Bond', amount:'', expiry:'', status:'Active' } } },
{ id: 'CBC', name: 'CBC Project (Addis)', contract: { amount:'', days:'', contractor:'',
consultant:'', start:'', planned:'', consultantCondition:'Supervision', notes:'', guarantee:{
type:'Performance Bond', amount:'', expiry:'', status:'Active' } } }
];
// ===== State =====
let state = { role:'engineer', current:'Harar', editIndex:null };
// ===== Storage helpers (local or Firebase later) =====
const LS = {
getProjects(){ const p = [Link]([Link]('cb_projects')||'null'); if(p)
return p; [Link]('cb_projects', [Link](defaultProjects)); return
defaultProjects; },
setProjects(p){ [Link]('cb_projects', [Link](p)); },
getTasks(id){ return [Link]([Link]('cb_tasks_'+id)||'[]'); },
setTasks(id, t){ [Link]('cb_tasks_'+id, [Link](t)); },
getMedia(id){ return [Link]([Link]('cb_media_'+id)||'[]'); },
setMedia(id, m){ [Link]('cb_media_'+id, [Link](m)); },
};
// ===== Auth =====
function login(){
[Link] = [Link]('role').value;
[Link]('loginBox').[Link]='none';
[Link]('app').[Link]='block';
initUI();
}
// ===== UI Init =====
function initUI(){
const sel = [Link]('projectSelect');
[Link]='';
[Link]().forEach(p=>{ const o=[Link]('option'); [Link]=[Link];
[Link]=[Link]; [Link](o); });
[Link] = [Link];
[Link]('taskToolbar').[Link] =
([Link]==='manager'||[Link]==='director')? 'flex':'none';
selectProject([Link]);
drawOverallChart();
}
function selectProject(id){
[Link] = id;
const info = [Link]().find(p=>[Link]===id).contract;
// Fill form
[Link]('cAmount').value = [Link]||'';
[Link]('cDays').value = [Link]||'';
[Link]('cContractor').value = [Link]||'';
[Link]('cConsultant').value = [Link]||'';
[Link]('cStart').value = [Link]||'';
[Link]('cPlanned').value = [Link]||'';
[Link]('cConsultantCondition').value =
[Link]||'Supervision';
[Link]('cNotes').value = [Link]||'';
[Link]('gType').value = [Link]?.type||'Performance Bond';
[Link]('gAmount').value = [Link]?.amount||'';
[Link]('gExpiry').value = [Link]?.expiry||'';
[Link]('gStatus').value = [Link]?.status||'Active';
renderKPIs();
renderTasks();
renderMedia();
buildGantt();
updateProjBar();
}
function saveContract(){
const projects = [Link]();
const idx = [Link](p=>[Link]===[Link]);
projects[idx].contract = {
amount: [Link]('cAmount').[Link](),
days: parseInt([Link]('cDays').value)||'',
contractor: [Link]('cContractor').[Link](),
consultant: [Link]('cConsultant').[Link](),
start: [Link]('cStart').value,
planned: [Link]('cPlanned').value,
consultantCondition: [Link]('cConsultantCondition').value,
notes: [Link]('cNotes').[Link](),
guarantee: {
type: [Link]('gType').value,
amount: [Link]('gAmount').[Link](),
expiry: [Link]('gExpiry').value,
status: [Link]('gStatus').value
}
};
[Link](projects);
renderKPIs();
}
// ===== KPIs =====
function renderKPIs(){
const box = [Link]('kpiBox');
const info = [Link]().find(p=>[Link]===[Link]).contract;
const tasks = [Link]([Link]);
const total = [Link]; const done = [Link](t=>[Link]==='Completed').length;
const delayed = [Link](t=>[Link]==='Delayed').length;
const progress = total? [Link](done/total*100) : 0;
const today = new Date();
const elapsed = [Link]? [Link](0, [Link]((today-parse([Link]))/DAY)) : 0;
const remain = ([Link] && [Link] && [Link])? [Link](0, [Link] -
elapsed) : '';
[Link] = `
<div class="item"><div class="label">Contract Amount</div><div
class="value">${[Link]||'—'} Birr</div></div>
<div class="item"><div class="label">Duration</div><div class="value">${[Link]||'—
'} days</div></div>
<div class="item"><div class="label">Contractor</div><div
class="value">${[Link]||'—'}</div></div>
<div class="item"><div class="label">Consultant</div><div
class="value">${[Link]||'—'} (${[Link]||'—'})</div></div>
<div class="item"><div class="label">Start → Planned</div><div
class="value">${[Link]||'—'} → ${[Link]||'—'}</div></div>
<div class="item"><div class="label">Elapsed / Remaining</div><div
class="value">${elapsed}d / ${remain||'—'}d</div></div>
<div class="item"><div class="label">Guarantee</div><div
class="value">${[Link]?.type||'—'} — ${[Link]?.amount||'—'} Birr (exp:
${[Link]?.expiry||'—'}, ${[Link]?.status||'—'})</div></div>
<div class="item"><div class="label">Progress</div><div
class="value">${progress}%</div></div>
<div class="item"><div class="label">Risks</div><div class="value">${delayed>0?
delayed+' delayed task(s)':'None'}</div></div>`;
}
function updateProjBar(){
const tasks = [Link]([Link]); const total = [Link]; const done =
[Link](t=>[Link]==='Completed').length;
const pct = total? [Link](done/total*100) : 0;
const bar = [Link]('projBar');
[Link] = pct+'%'; [Link] = pct+'%';
[Link] = pct>=80? 'var(--success)' : pct>=50? 'var(--info)' : pct>0? 'var(--
warning)':'var(--muted)';
}
// ===== Tasks (CRUD) =====
function renderTasks(){
const body = [Link]('taskBody'); [Link]='';
[Link]([Link]).forEach((t,i)=>{
const tr=[Link]('tr');
[Link] = `
<td>${[Link]}</td>
<td>${[Link]}</td>
<td>${[Link]}</td>
<td>${[Link]}</td>
<td>${[Link]}</td>
<td class="actions">
<button onclick="startEdit(${i})">Edit</button>
<button class="secondary" onclick="delTask(${i})">Delete</button>
</td>`;
[Link](tr);
});
}
function saveTask(){
const name = [Link]('tName').[Link]();
const resp = [Link]('tResp').[Link]();
const start = [Link]('tStart').value; const end =
[Link]('tEnd').value;
const status = [Link]('tStatus').value;
if(!name||!resp||!start||!end){ alert('Fill task, responsible, start, end'); return; }
if(new Date(end) < new Date(start)){ alert('End must be after Start'); return; }
const tasks = [Link]([Link]);
if([Link]===null){ [Link]({ task:name, responsible:resp, start, end, status
}); }
else { tasks[[Link]] = { task:name, responsible:resp, start, end, status }; }
[Link]([Link], tasks);
clearTaskForm(); renderTasks(); buildGantt(); updateProjBar(); renderKPIs();
}
function startEdit(i){
const t = [Link]([Link])[i]; [Link] = i;
[Link]('tName').value=[Link];
[Link]('tResp').value=[Link];
[Link]('tStart').value=[Link];
[Link]('tEnd').value=[Link];
[Link]('tStatus').value=[Link];
[Link]('tSave').textContent='Update Task';
[Link]('tCancel').[Link]='inline-block';
}
function cancelEdit(){ clearTaskForm(); }
function clearTaskForm(){ [Link]=null;
['tName','tResp','tStart','tEnd'].forEach(id=>[Link](id).value='');
[Link]('tStatus').value='Pending';
[Link]('tSave').textContent='Add Task';
[Link]('tCancel').[Link]='none'; }
function delTask(i){ if(!confirm('Delete task?')) return; const t=[Link]([Link]);
[Link](i,1); [Link]([Link],t); renderTasks(); buildGantt(); updateProjBar();
renderKPIs(); }
// ===== Media (images & videos) =====
function renderMedia(){
const wrap = [Link]('gallery'); [Link]='';
const media = [Link]([Link]);
[Link](m=>{
const d=[Link]('div'); [Link]='thumb';
if([Link]('video')){
[Link] = `<video src="${[Link]}" controls></video><div
class='cap'>${[Link]||''}</div>`;
} else {
[Link] = `<img src="${[Link]}" alt="media"
onclick="[Link]('${[Link]}','_blank')"/><div class='cap'>${[Link]||''}</div>`;
}
[Link](d);
});
}
function addMedia(){
const input = [Link]('mediaInput'); const caption =
[Link]('mediaCaption').[Link]();
if(![Link]) return;
const files = [Link]([Link]);
const existing = [Link]([Link]);
// If Firebase enabled, upload to Storage; else base64 to localStorage
if(window.__FB__?.USE_FIREBASE){
const { storage, ref, uploadBytes, getDownloadURL } = window.__FB__;
[Link]([Link](async (f)=>{
const r = ref(storage, `${[Link]}/${[Link]()}_${[Link]}`);
const snap = await uploadBytes(r, f);
const url = await getDownloadURL([Link]);
[Link]({ type: [Link], url, caption });
})).then(()=>{ [Link]([Link], existing); renderMedia(); [Link]='';
[Link]('mediaCaption').value=''; });
} else {
let remaining = [Link];
[Link](f=>{
const reader = new FileReader();
[Link] = (e)=>{ [Link]({ type: [Link], url: [Link], caption });
remaining--; if(remaining===0){ [Link]([Link], existing); renderMedia();
[Link]=''; [Link]('mediaCaption').value=''; } };
[Link](f);
});
}
}
// ===== Gantt =====
function buildGantt(){
const gDays = [Link]('gDays'); const gBars =
[Link]('gBars'); const gLeft = [Link]('gLeft');
[Link]=''; [Link]=''; [Link]='';
const tasks = [Link]([Link]); if(![Link]) return;
const starts = [Link](t=>parse([Link])); const ends = [Link](t=>parse([Link]));
const minS = new Date([Link](...starts)); const maxE = new Date([Link](...ends));
const start = new Date([Link]() - 3*DAY); const end = new Date([Link]() +
3*DAY);
const days = [Link]((end-start)/DAY)+1; const dayW = 28;
[Link] = `repeat(${days}, ${dayW}px)`;
for(let i=0;i<days;i++){ const d=[Link]('div'); [Link]='day-cell';
const date=new Date([Link]()+i*DAY); const n=[Link](); [Link] =
n%5===0? n:''; [Link](d); }
const width = days*dayW; [Link] = width+'px';
[Link](t=>{ const lr=[Link]('div'); [Link]='lrow';
[Link]=[Link]; [Link](lr);
const br=[Link]('div'); [Link]='bar-row';
const s=parse([Link]); const e=parse([Link]); const off=[Link]((s-start)/DAY);
const dur=[Link](1, [Link]((e-s)/DAY)+1);
const bar=[Link]('div'); [Link]='bar '+cls([Link]);
[Link]=(off*dayW)+'px'; [Link]=(dur*dayW)+'px'; [Link]=`${[Link]} (
${[Link]} → ${[Link]} — ${[Link]} )`; [Link]=[Link]; [Link](bar);
[Link](br); });
const today=new Date(); const diff = [Link]((today-start)/DAY);
[Link]('gRight').scrollLeft = clamp((diff-4)*dayW, 0, width);
function cls(s){ switch((s||'').toLowerCase()){ case 'completed':return 'completed'; case
'in progress':return 'progress'; case 'delayed':return 'delayed'; default:return 'pending'; } }
}
// ===== Charts =====
function drawOverallChart(){
const ctx = [Link]('progressChart').getContext('2d');
const labels = [Link]().map(p=>[Link]);
const data = [Link](id=>{ const t=[Link](id); return [Link]?
[Link]([Link](x=>[Link]==='Completed').length/[Link]*100):0; });
new Chart(ctx, { type:'bar', data:{ labels, datasets:[{ label:'Completion %', data,
backgroundColor:['#f59e0b','#3b82f6','#ef4444','#10b981','#6366f1'] }] }, options:{
responsive:true, plugins:{ legend:{ display:false } }, scales:{ y:{ beginAtZero:true, max:100 } }
} });
}
// ===== Export =====
function exportExcel(){
const proj = [Link]; const info = [Link]().find(p=>[Link]===proj).contract;
const tasks = [Link](proj);
const sheet = [
['Project', proj],
['Contract Amount (Birr)', [Link]||''],
['Contract Duration (days)', [Link]||''],
['Contractor', [Link]||''],
['Consultant', `${[Link]||''} (${[Link]||''})`],
['Start', [Link]||''],
['Planned Finish', [Link]||''],
['Guarantee Type', [Link]?.type||''],
['Guarantee Amount (Birr)', [Link]?.amount||''],
['Guarantee Expiry', [Link]?.expiry||''],
['Guarantee Status', [Link]?.status||''],
[],
['Task','Responsible','Start','End','Status']
];
[Link](t=> [Link]([[Link], [Link], [Link], [Link], [Link]]));
const wb = [Link].book_new(); const ws = [Link].aoa_to_sheet(sheet);
[Link].book_append_sheet(wb, ws, proj); [Link](wb, `CB_${proj}_report.xlsx`);
}
function exportPDF(){
const { jsPDF } = [Link]; const doc = new jsPDF('p','pt','a4');
const proj = [Link]; const info = [Link]().find(p=>[Link]===proj).contract;
[Link](16); [Link](`Project Report — ${proj}`, 40, 40);
const meta = [
['Contract Amount (Birr)', [Link]||''],
['Contract Duration (days)', [Link]||''],
['Contractor', [Link]||''],
['Consultant', `${[Link]||''} (${[Link]||''})`],
['Start', [Link]||''],
['Planned Finish', [Link]||''],
['Guarantee', `${[Link]?.type||''} — ${[Link]?.amount||''} Birr (exp:
${[Link]?.expiry||''}, ${[Link]?.status||''})`]
];
[Link]({ startY: 60, head:[['Field','Value']], body: meta, theme:'grid' });
const taskRows = [Link](proj).map(t=>[[Link],[Link],[Link],[Link],[Link]]);
[Link]({ startY: [Link]+18,
head:[['Task','Responsible','Start','End','Status']], body: taskRows, theme:'grid' });
[Link](`CB_${proj}_report.pdf`);
}
// ===== Boot (seed some demo tasks) =====
[Link]('load', ()=>{
const projs = [Link]();
[Link](p=>{
const k='cb_tasks_'+[Link]; if(){
if([Link]==='Harar') [Link]([Link],[
{ task:'2nd Floor Casting', responsible:'Site Engineer', start:'2025-08-10', end:'2025-
08-30', status:'In Progress' },
{ task:'Procurement: Rebar', responsible:'Procurement', start:'2025-08-15', end:'2025-
09-05', status:'Delayed' },
{ task:'Permit Approval', responsible:'Admin', start:'2025-08-01', end:'2025-08-22',
status:'Completed' }
]);
if([Link]==='Adama') [Link]([Link],[{ task:'Foundation Work', responsible:'PM',
start:'2025-08-05', end:'2025-09-18', status:'In Progress' }]);
if([Link]==='CBC') [Link]([Link],[{ task:'Design Review', responsible:'Design Team',
start:'2025-08-20', end:'2025-08-28', status:'Pending' }]);
}
});
});
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Coop Bank Project Dashboard</title>
<style>
body {
font-family: "Segoe UI", Arial, sans-serif;
margin: 0;
background: #f4f6f9;
color: #333;
}
header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 2rem;
background: #003366;
color: white;
}
header h1 {
font-size: 20px;
}
.logout-btn {
background: #cc0000;
color: white;
border: none;
padding: 6px 12px;
border-radius: 6px;
cursor: pointer;
}
.container {
padding: 20px;
}
/* Project name editable */
.project-header {
display: flex;
align-items: center;
margin-bottom: 1rem;
}
.project-title {
font-size: 22px;
font-weight: bold;
margin-right: 10px;
}
.edit-btn {
cursor: pointer;
font-size: 16px;
color: #0078d4;
}
/* Export buttons */
.export-icons {
margin-top: 10px;
}
.export-icons button {
border: none;
background: none;
cursor: pointer;
font-size: 20px;
margin-right: 10px;
}
/* Task Cards */
.task-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 15px;
margin: 20px 0;
}
.task-card {
background: white;
border-radius: 10px;
padding: 15px;
box-shadow: 0 2px 6px rgba(0,0,0,0.1);
}
.task-card h3 {
margin: 0;
}
.badge {
padding: 3px 8px;
border-radius: 6px;
font-size: 12px;
color: white;
}
.[Link] { background: #ff9900; }
.[Link] { background: #0078d4; }
.[Link] { background: #28a745; }
.progress-bar {
height: 8px;
border-radius: 5px;
background: #ddd;
margin-top: 8px;
overflow: hidden;
}
.progress-bar div {
height: 100%;
background: #0078d4;
}
/* Documents */
.doc-section {
margin-top: 30px;
}
.doc-category {
margin-bottom: 20px;
}
.doc-list {
list-style: none;
padding: 0;
}
.doc-list li {
margin: 5px 0;
}
.doc-link {
cursor: pointer;
color: #0078d4;
text-decoration: underline;
}
/* Modal */
.modal {
display: none;
position: fixed;
top:0; left:0; width:100%; height:100%;
background: rgba(0,0,0,0.6);
justify-content: center;
align-items: center;
}
.modal-content {
background: white;
padding: 20px;
border-radius: 8px;
width: 80%;
height: 80%;
overflow: auto;
}
.modal iframe, .modal img {
width: 100%;
height: 90%;
border: none;
}
.close-btn {
background: #cc0000;
color: white;
border: none;
padding: 5px 10px;
margin-bottom: 10px;
cursor: pointer;
}
</style>
</head>
<body>
<header>
<h1>Building Construction Management & Administration Dashboard</h1>
<button class="logout-btn" onclick="logout()">Logout</button>
</header>
<div class="container">
<!-- Project Name -->
<div class="project-header">
<span id="projectTitle" class="project-title">Harar B+G+6 Building Project</span>
<span class="edit-btn" onclick="editProjectName()"> </span>
</div>
<!-- Export icons -->
<div class="export-icons">
<button title="Export to Excel"> </button>
<button title="Export to PDF"> </button>
</div>
<!-- Task Monitoring -->
<h2>Task Monitoring</h2>
<div class="task-grid" id="taskGrid"></div>
<!-- Documents Upload -->
<div class="doc-section">
<h2>Project Documents</h2>
<input type="file" id="fileInput" multiple onchange="uploadDocs()"/>
<div id="docContainer"></div>
</div>
</div>
<!-- Modal for preview -->
<div class="modal" id="previewModal">
<div class="modal-content">
<button class="close-btn" onclick="closeModal()">Close</button>
<div id="previewContent"></div>
</div>
</div>
<script>
let tasks = [
{name:"Foundation Work", status:"completed", progress:100},
{name:"Structural Work", status:"progress", progress:65},
{name:"Finishing Work", status:"pending", progress:0}
];
let docs = {
"Designs": [],
"Reports": [],
"Minutes": [],
"Letters": [],
"Material Approvals": []
};
function renderTasks() {
const grid = [Link]("taskGrid");
[Link] = "";
[Link](t=>{
let card = [Link]("div");
[Link] = "task-card";
[Link] = `
<h3>${[Link]}</h3>
<span class="badge ${[Link]}">
${[Link](0).toUpperCase()+[Link](1)}
</span>
<div class="progress-bar"><div style="width:${[Link]}%;"></div></div>
`;
[Link](card);
});
}
function uploadDocs() {
const input = [Link]("fileInput");
const files = [Link];
for(let f of files){
// Assign randomly for demo
let catNames = [Link](docs);
let cat = catNames[[Link]([Link]()*[Link])];
docs[cat].push(f);
}
renderDocs();
}
function renderDocs(){
const container = [Link]("docContainer");
[Link] = "";
for(let cat in docs){
let section = [Link]("div");
[Link] = "doc-category";
[Link] = `<h3>${cat}</h3>`;
let ul = [Link]("ul");
[Link] = "doc-list";
docs[cat].forEach((f,i)=>{
let li = [Link]("li");
[Link] = `<span class="doc-link"
onclick="previewDoc('${cat}',${i})">${[Link]}</span>`;
[Link](li);
});
[Link](ul);
[Link](section);
}
}
function previewDoc(cat, index){
let f = docs[cat][index];
let url = [Link](f);
let modal = [Link]("previewModal");
let content = [Link]("previewContent");
if([Link]("pdf")){
[Link] = `<iframe src="${url}"></iframe>`;
} else if([Link]("image")){
[Link] = `<img src="${url}" />`;
} else {
[Link] = `<p>No preview available. File: ${[Link]}</p>`;
}
[Link] = "flex";
}
function closeModal(){
[Link]("previewModal").[Link]="none";
[Link]("previewContent").innerHTML="";
}
function editProjectName(){
let titleSpan = [Link]("projectTitle");
let current = [Link];
[Link] = `<input type='text' id='editTitle' value='${current}' />
<button onclick='saveProjectName()'>Save</button>`;
}
function saveProjectName(){
let val = [Link]("editTitle").value;
[Link]("projectTitle").innerText = val;
}
function logout(){
alert("You have been logged out!");
// [Link](); // Firebase ready
[Link]();
}
renderTasks();
renderDocs();
</script>
</body>
</html>