Files
Fire-alarm-managment/app/templates/schedule.html
2026-01-19 21:57:25 -05:00

183 lines
7.4 KiB
HTML

{% extends "base.html" %}
{% block title %}Schedule - Fire Alarm Management{% endblock %}
{% block content %}
<div class="row mb-4">
<div class="col">
<h1><i class="bi bi-calendar3"></i> Schedule Overview</h1>
</div>
</div>
<!-- Filters -->
<div class="row mb-4">
<div class="col-md-3">
<select id="jobFilter" class="form-select">
<option value="">All Jobs</option>
</select>
</div>
<div class="col-md-3">
<select id="phaseTypeFilter" class="form-select">
<option value="">All Phase Types</option>
<option value="rough_in">Rough-in</option>
<option value="trim">Trim</option>
<option value="commissioning">Commissioning</option>
<option value="final">Final</option>
<option value="turnover">Turnover</option>
</select>
</div>
<div class="col-md-3">
<select id="statusFilter" class="form-select">
<option value="">All Status</option>
<option value="completed">Completed</option>
<option value="in_progress">In Progress</option>
</select>
</div>
</div>
<!-- Schedule Cards by Job -->
<div id="scheduleContainer" class="row">
<div class="col text-center">
<div class="spinner-border" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
let allJobs = [];
let allPhases = {};
async function loadSchedule() {
try {
// Load all jobs
const jobsResponse = await fetch('/api/jobs');
allJobs = await jobsResponse.json();
// Populate job filter
const jobFilter = document.getElementById('jobFilter');
allJobs.forEach(job => {
const option = document.createElement('option');
option.value = job.id;
option.textContent = `${job.job_number} - ${job.job_name}`;
jobFilter.appendChild(option);
});
// Load phases for each job
for (const job of allJobs) {
const phasesResponse = await fetch(`/api/jobs/${job.id}/phases`);
allPhases[job.id] = await phasesResponse.json();
}
renderSchedule();
} catch (error) {
console.error('Error loading schedule:', error);
document.getElementById('scheduleContainer').innerHTML =
'<div class="col"><div class="alert alert-danger">Error loading schedule</div></div>';
}
}
function renderSchedule() {
const container = document.getElementById('scheduleContainer');
const jobFilter = document.getElementById('jobFilter').value;
const phaseTypeFilter = document.getElementById('phaseTypeFilter').value;
const statusFilter = document.getElementById('statusFilter').value;
let jobs = jobFilter ? allJobs.filter(j => j.id == jobFilter) : allJobs;
if (jobs.length === 0) {
container.innerHTML = '<div class="col"><div class="alert alert-info">No jobs found</div></div>';
return;
}
container.innerHTML = jobs.map(job => {
let phases = allPhases[job.id] || [];
if (phaseTypeFilter) {
phases = phases.filter(p => p.phase_type === phaseTypeFilter);
}
if (statusFilter) {
phases = phases.filter(p =>
(statusFilter === 'completed' && p.completed) ||
(statusFilter === 'in_progress' && !p.completed)
);
}
if (phases.length === 0 && (phaseTypeFilter || statusFilter)) {
return '';
}
const phaseTypes = ['rough_in', 'trim', 'commissioning', 'final', 'turnover'];
const phaseLabels = {
'rough_in': 'Rough-in',
'trim': 'Trim',
'commissioning': 'Commissioning',
'final': 'Final',
'turnover': 'Turnover'
};
return `
<div class="col-12 mb-4">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<span>
<strong>${job.job_number}</strong> - ${job.job_name}
<span class="badge bg-info">${(job.percent_complete * 100).toFixed(0)}% Complete</span>
</span>
<a href="/jobs/${job.id}" class="btn btn-sm btn-outline-primary">View Job</a>
</div>
<div class="card-body">
${phases.length === 0 ? '<p class="text-muted">No phases scheduled yet</p>' : `
<div class="row">
${phaseTypes.map(type => {
const typePhases = phases.filter(p => p.phase_type === type);
if (typePhases.length === 0) return '';
return `
<div class="col-md-4 mb-3">
<h6 class="text-uppercase text-muted">${phaseLabels[type]}</h6>
${typePhases.map(p => `
<div class="card mb-2 ${p.completed ? 'border-success' : ''}">
<div class="card-body py-2 px-3">
<div class="d-flex justify-content-between align-items-center">
<span>Phase ${p.phase_number}</span>
<span class="badge ${p.completed ? 'bg-success' : 'bg-secondary'}">
${p.completed ? 'Done' : 'Pending'}
</span>
</div>
<small class="text-muted">
${p.points ? p.points + ' pts' : ''}
${p.start_date ? ' | Start: ' + p.start_date : ''}
${p.due_date ? ' | Due: ' + p.due_date : ''}
</small>
${p.men_on_site ? '<br><small>Men: ' + p.men_on_site + '</small>' : ''}
</div>
</div>
`).join('')}
</div>
`;
}).join('')}
</div>
`}
</div>
</div>
</div>
`;
}).join('');
if (!container.innerHTML.trim()) {
container.innerHTML = '<div class="col"><div class="alert alert-info">No matching phases found</div></div>';
}
}
document.addEventListener('DOMContentLoaded', () => {
loadSchedule();
document.getElementById('jobFilter').addEventListener('change', renderSchedule);
document.getElementById('phaseTypeFilter').addEventListener('change', renderSchedule);
document.getElementById('statusFilter').addEventListener('change', renderSchedule);
});
</script>
{% endblock %}