Checkpoint-1 feedback from Konrad — three row actions on the Adjustments tab were breaking his muscle memory vs the Pending tab: 1. Worker name used to navigate to /workers/<id>/. Now opens the Worker Lookup modal using the existing .worker-lookup-link handler already bound on the dashboard — zero new JS. 2. Eye icon on PAID rows used to navigate to /payroll/payslip/<pk>/. Now opens the same #previewPayslipModal that unpaid rows use (via the existing .preview-payslip-btn handler). The 'Paid #N' green badge in the Status column still links to the historical payslip detail page, so both entry points coexist. 3. Project name used to open the Profile tab of the project detail page; now includes the #history URL fragment so the History tab is active. Added a tiny DOMContentLoaded helper in projects/detail.html that activates whatever tab the hash points to — generalised so any future deep-link works (#history, #supervisors, #teams, #workers).
203 lines
11 KiB
HTML
203 lines
11 KiB
HTML
{% extends 'base.html' %}
|
|
{% load format_tags %}
|
|
|
|
{% block title %}{{ project.name }} | Projects | FoxFitt{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container py-4">
|
|
|
|
<div class="d-flex flex-column flex-md-row justify-content-between align-items-start align-items-md-center mb-4">
|
|
<div>
|
|
<h1 class="page-title">
|
|
<i class="fas fa-project-diagram me-2" style="color: var(--accent);"></i>{{ project.name }}
|
|
{% if project.active %}<span class="badge ms-2" style="background: rgba(16, 185, 129, 0.15); color: #10b981; font-size: 0.6em; vertical-align: middle;">Active</span>
|
|
{% else %}<span class="badge ms-2" style="background: rgba(148, 163, 184, 0.15); color: var(--text-secondary); font-size: 0.6em; vertical-align: middle;">Inactive</span>{% endif %}
|
|
</h1>
|
|
<p class="mb-0" style="color: var(--text-secondary); font-size: 0.9rem;">
|
|
{{ project.supervisors.count }} supervisor{{ project.supervisors.count|pluralize }}
|
|
{% if project.start_date or project.end_date %}
|
|
| {{ project.start_date|date:"d M Y"|default:'?' }} → {{ project.end_date|date:"d M Y"|default:'ongoing' }}
|
|
{% endif %}
|
|
</p>
|
|
</div>
|
|
<div class="d-flex gap-2 mt-3 mt-md-0">
|
|
<a href="{% url 'project_edit' project.id %}" class="btn btn-accent shadow-sm"><i class="fas fa-pencil-alt me-1"></i>Edit</a>
|
|
<a href="{% url 'project_list' %}" class="btn btn-outline-secondary"><i class="fas fa-arrow-left me-1"></i>Back</a>
|
|
</div>
|
|
</div>
|
|
|
|
<ul class="nav nav-tabs mb-3" role="tablist">
|
|
<li class="nav-item"><button class="nav-link active" data-bs-toggle="tab" data-bs-target="#profile" type="button">Profile</button></li>
|
|
<li class="nav-item"><button class="nav-link" data-bs-toggle="tab" data-bs-target="#supervisors" type="button">Supervisors <span class="badge bg-secondary ms-1">{{ project.supervisors.count }}</span></button></li>
|
|
<li class="nav-item"><button class="nav-link" data-bs-toggle="tab" data-bs-target="#teams" type="button">Teams <span class="badge bg-secondary ms-1">{{ teams_worked.count }}</span></button></li>
|
|
<li class="nav-item"><button class="nav-link" data-bs-toggle="tab" data-bs-target="#workers" type="button">Workers <span class="badge bg-secondary ms-1">{{ workers_worked.count }}</span></button></li>
|
|
<li class="nav-item"><button class="nav-link" data-bs-toggle="tab" data-bs-target="#history" type="button">History</button></li>
|
|
</ul>
|
|
|
|
<div class="tab-content">
|
|
|
|
<div class="tab-pane fade show active" id="profile">
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<dl class="row mb-0" style="font-size: 0.9rem;">
|
|
<dt class="col-sm-3">Name</dt> <dd class="col-sm-9 fw-semibold">{{ project.name }}</dd>
|
|
<dt class="col-sm-3">Description</dt> <dd class="col-sm-9" style="color: var(--text-secondary);">{{ project.description|default:'—'|linebreaksbr }}</dd>
|
|
<dt class="col-sm-3">Start Date</dt> <dd class="col-sm-9">{{ project.start_date|date:"d M Y"|default:'—' }}</dd>
|
|
<dt class="col-sm-3">End Date</dt> <dd class="col-sm-9">{{ project.end_date|date:"d M Y"|default:'—' }}</dd>
|
|
<dt class="col-sm-3">Active</dt> <dd class="col-sm-9">{% if project.active %}<span class="text-success"><i class="fas fa-check-circle me-1"></i>Yes</span>{% else %}<span class="text-muted">No</span>{% endif %}</dd>
|
|
</dl>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="tab-pane fade" id="supervisors">
|
|
<div class="card">
|
|
<div class="card-body p-0">
|
|
{% if project.supervisors.all %}
|
|
<table class="table table-hover mb-0">
|
|
<thead><tr><th>Username</th><th>Full Name</th><th>Email</th></tr></thead>
|
|
<tbody>
|
|
{% for s in project.supervisors.all %}
|
|
<tr>
|
|
<td class="fw-medium">{{ s.username }}</td>
|
|
<td>{{ s.get_full_name|default:'—' }}</td>
|
|
<td style="color: var(--text-secondary); font-size: 0.85rem;">{{ s.email|default:'—' }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
{% else %}
|
|
<p class="text-muted text-center py-4 mb-0">No supervisors assigned. <a href="{% url 'project_edit' project.id %}">Assign some</a>.</p>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="tab-pane fade" id="teams">
|
|
<div class="card">
|
|
<div class="card-body p-0">
|
|
{% if teams_worked %}
|
|
<table class="table table-hover mb-0">
|
|
<thead><tr><th>Team</th><th>Supervisor</th><th class="text-end">Workers</th></tr></thead>
|
|
<tbody>
|
|
{% for t in teams_worked %}
|
|
<tr>
|
|
<td class="fw-medium"><a href="{% url 'team_detail' t.id %}" style="color: var(--text-main); text-decoration: none;">{{ t.name }}</a></td>
|
|
<td style="color: var(--text-secondary);">{{ t.supervisor.username|default:'—' }}</td>
|
|
<td class="text-end">{{ t.workers.count }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
{% else %}
|
|
<p class="text-muted text-center py-4 mb-0">No teams have logged work on this project.</p>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="tab-pane fade" id="workers">
|
|
<div class="card">
|
|
<div class="card-body p-0">
|
|
{% if workers_worked %}
|
|
<table class="table table-hover mb-0">
|
|
<thead><tr><th>Worker</th><th>ID</th><th class="text-end">Salary</th></tr></thead>
|
|
<tbody>
|
|
{% for w in workers_worked %}
|
|
<tr>
|
|
<td class="fw-medium"><a href="{% url 'worker_detail' w.id %}" style="color: var(--text-main); text-decoration: none;">{{ w.name }}</a></td>
|
|
<td style="color: var(--text-secondary); font-size: 0.85rem;">{{ w.id_number }}</td>
|
|
<td class="text-end">R {{ w.monthly_salary|money }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
{% else %}
|
|
<p class="text-muted text-center py-4 mb-0">No workers have logged work on this project.</p>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="tab-pane fade" id="history">
|
|
<div class="row g-3">
|
|
<div class="col-lg-6">
|
|
<div class="card h-100">
|
|
<div class="card-header"><h6 class="m-0 fw-bold">Activity Summary</h6></div>
|
|
<div class="card-body">
|
|
<dl class="row mb-0" style="font-size: 0.9rem;">
|
|
<dt class="col-sm-7">Total Days Worked</dt> <dd class="col-sm-5 fw-semibold">{{ days_worked }}</dd>
|
|
<dt class="col-sm-7">Total Labour Cost</dt> <dd class="col-sm-5 fw-semibold">R {{ total_labour_cost|money }}</dd>
|
|
<dt class="col-sm-7">First Activity</dt> <dd class="col-sm-5">{{ first_activity|date:"d M Y"|default:'—' }}</dd>
|
|
<dt class="col-sm-7">Last Activity</dt> <dd class="col-sm-5">{{ last_activity|date:"d M Y"|default:'—' }}</dd>
|
|
</dl>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-lg-6">
|
|
<div class="card h-100">
|
|
<div class="card-header"><h6 class="m-0 fw-bold">Labour Cost by Team</h6></div>
|
|
<div class="card-body p-0">
|
|
{% if cost_breakdown %}
|
|
<table class="table table-sm mb-0">
|
|
<thead><tr><th>Team</th><th class="text-end">Days</th><th class="text-end">Cost</th></tr></thead>
|
|
<tbody>
|
|
{% for c in cost_breakdown %}
|
|
<tr>
|
|
<td>{{ c.team }}</td>
|
|
<td class="text-end">{{ c.worker_days }}</td>
|
|
<td class="text-end fw-semibold">R {{ c.total|money }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
{% else %}<p class="text-muted text-center py-3 mb-0">No work logs yet.</p>{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header"><h6 class="m-0 fw-bold">Recent Work Logs (last 10)</h6></div>
|
|
<div class="card-body p-0">
|
|
{% if recent_logs %}
|
|
<table class="table table-sm mb-0">
|
|
<thead><tr><th>Date</th><th>Team</th><th class="text-end">Workers</th></tr></thead>
|
|
<tbody>
|
|
{% for log in recent_logs %}
|
|
<tr {% if user.is_staff or user.is_superuser %}class="work-log-row" data-log-id="{{ log.id }}" style="cursor: pointer;"{% endif %}>
|
|
<td>{{ log.date|date:"d M Y" }}</td>
|
|
<td>{{ log.team.name|default:'—' }}</td>
|
|
<td class="text-end">{{ log.workers.count }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
{% else %}<p class="text-muted text-center py-3 mb-0">No work logs yet.</p>{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<script>
|
|
// === Activate tab from URL hash ===
|
|
// When the page loads with #history / #profile / #supervisors / #teams /
|
|
// #workers in the URL, open that tab automatically. This lets other
|
|
// pages link directly to a specific tab — e.g. the Adjustments row
|
|
// partial links to /projects/<id>/#history so a user clicking a project
|
|
// name from an adjustment lands on the relevant history view.
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
if (!window.location.hash) return;
|
|
var target = document.querySelector(
|
|
'[data-bs-toggle="tab"][data-bs-target="' + window.location.hash + '"]'
|
|
);
|
|
if (target) {
|
|
new bootstrap.Tab(target).show();
|
|
}
|
|
});
|
|
</script>
|
|
{% endblock %}
|