Files
enterprise_digital-platform/IT_asset_management/app/templates/maintenance/index.html
T
ske087 ca0d48d4b1 Fix 404 on maintenance update: use url_for instead of hardcoded path
Hardcoded '/maintenance/{id}/update' ignored the '/itassets/' nginx prefix.
Generate the base URL with url_for and replace the placeholder id in JS.
2026-07-08 23:29:02 +03:00

329 lines
14 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends 'base.html' %}
{% block title %}Maintenance IT Asset Management{% endblock %}
{% block breadcrumb %}
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.index') }}">Home</a></li>
<li class="breadcrumb-item active">Maintenance</li>
{% endblock %}
{% block extra_head %}
<style>
.status-badge-open { background: #ffc107; color: #000; }
.status-badge-in_progress { background: #0d6efd; color: #fff; }
.status-badge-closed { background: #198754; color: #fff; }
tr.clickable-row { cursor: pointer; }
tr.clickable-row:hover td { background: #f0f5ff; }
</style>
{% endblock %}
{% block content %}
<div class="page-header d-flex align-items-center justify-content-between mb-4">
<h1><i class="bi bi-wrench-adjustable me-2"></i>Maintenance</h1>
{% if current_user.is_editor %}
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#maintenanceModal"
id="btnAddRecord">
<i class="bi bi-plus-circle me-1"></i>Add Record
</button>
{% endif %}
</div>
<!-- Filters -->
<form method="GET" class="row g-2 mb-3">
<div class="col-auto">
<select name="status" class="form-select form-select-sm" onchange="this.form.submit()">
<option value="">All statuses</option>
{% for val, label in statuses %}
<option value="{{ val }}" {% if status_filter == val %}selected{% endif %}>{{ label }}</option>
{% endfor %}
</select>
</div>
<div class="col-auto">
<select name="asset_id" class="form-select form-select-sm" onchange="this.form.submit()">
<option value="">All assets</option>
{% for a in all_assets %}
<option value="{{ a.id }}" {% if asset_filter == a.id %}selected{% endif %}>
{{ a.serial_number }}{% if a.brand %} {{ a.brand }}{% endif %}{% if a.model %} {{ a.model }}{% endif %}
</option>
{% endfor %}
</select>
</div>
<div class="col-auto">
<a href="{{ url_for('maintenance.index') }}" class="btn btn-sm btn-outline-secondary">Clear</a>
</div>
</form>
<!-- Table -->
<div class="card border-0 shadow-sm">
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead class="table-light">
<tr>
<th>Asset</th>
<th>Start Date</th>
<th>End Date</th>
<th>Status</th>
<th>Initial Diagnosis</th>
<th>Action Taken</th>
<th>Result</th>
{% if current_user.is_editor %}<th></th>{% endif %}
</tr>
</thead>
<tbody>
{% for r in pagination.items %}
<tr class="{% if current_user.is_editor %}clickable-row{% endif %}"
{% if current_user.is_editor %}
data-bs-toggle="modal" data-bs-target="#maintenanceModal"
data-id="{{ r.id }}"
data-asset="{{ r.asset_id }}"
data-start="{{ r.start_date.isoformat() if r.start_date else '' }}"
data-end="{{ r.end_date.isoformat() if r.end_date else '' }}"
data-status="{{ r.status }}"
data-diagnosis="{{ r.initial_diagnosis or '' }}"
data-action="{{ r.action_taken or '' }}"
data-result="{{ r.action_result or '' }}"
{% endif %}>
<td>
<a href="{{ url_for('assets.detail', asset_id=r.asset.id) }}" onclick="event.stopPropagation()">
<strong>{{ r.asset.serial_number }}</strong>
</a>
{% if r.asset.brand %}<br><small class="text-muted">{{ r.asset.brand }} {{ r.asset.model or '' }}</small>{% endif %}
</td>
<td>{{ r.start_date.strftime('%d/%m/%Y') if r.start_date else '—' }}</td>
<td>{{ r.end_date.strftime('%d/%m/%Y') if r.end_date else '—' }}</td>
<td>
<span class="badge status-badge-{{ r.status }}">
{% for val, label in statuses %}{% if val == r.status %}{{ label }}{% endif %}{% endfor %}
</span>
</td>
<td>
<span title="{{ r.initial_diagnosis or '' }}">
{{ (r.initial_diagnosis or '—')[:60] }}{% if r.initial_diagnosis and r.initial_diagnosis|length > 60 %}…{% endif %}
</span>
</td>
<td>
<span title="{{ r.action_taken or '' }}">
{{ (r.action_taken or '—')[:60] }}{% if r.action_taken and r.action_taken|length > 60 %}…{% endif %}
</span>
</td>
<td>
<span title="{{ r.action_result or '' }}">
{{ (r.action_result or '—')[:60] }}{% if r.action_result and r.action_result|length > 60 %}…{% endif %}
</span>
</td>
{% if current_user.is_editor %}
<td onclick="event.stopPropagation()">
<form method="POST" action="{{ url_for('maintenance.delete', record_id=r.id) }}"
onsubmit="return confirm('Delete this maintenance record?')">
<button type="submit" class="btn btn-sm btn-outline-danger py-0 px-2">
<i class="bi bi-trash"></i>
</button>
</form>
</td>
{% endif %}
</tr>
{% else %}
<tr>
<td colspan="{{ 8 if current_user.is_editor else 7 }}" class="text-center text-muted py-4">
No maintenance records found.
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- Pagination -->
{% if pagination.pages > 1 %}
<div class="card-footer bg-white d-flex justify-content-between align-items-center py-2">
<small class="text-muted">
Showing {{ pagination.first }}{{ pagination.last }} of {{ pagination.total }}
</small>
<nav>
<ul class="pagination pagination-sm mb-0">
{% if pagination.has_prev %}
<li class="page-item">
<a class="page-link" href="{{ url_for('maintenance.index', page=pagination.prev_num, status=status_filter, asset_id=asset_filter) }}"></a>
</li>
{% endif %}
{% for p in pagination.iter_pages(left_edge=1, right_edge=1, left_current=2, right_current=2) %}
{% if p %}
<li class="page-item {% if p == pagination.page %}active{% endif %}">
<a class="page-link" href="{{ url_for('maintenance.index', page=p, status=status_filter, asset_id=asset_filter) }}">{{ p }}</a>
</li>
{% else %}
<li class="page-item disabled"><span class="page-link"></span></li>
{% endif %}
{% endfor %}
{% if pagination.has_next %}
<li class="page-item">
<a class="page-link" href="{{ url_for('maintenance.index', page=pagination.next_num, status=status_filter, asset_id=asset_filter) }}"></a>
</li>
{% endif %}
</ul>
</nav>
</div>
{% endif %}
</div>
<!-- ═══ Maintenance Record Modal (Add / Edit) ═══════════════════════════════ -->
{% if current_user.is_editor %}
<div class="modal fade" id="maintenanceModal" tabindex="-1" aria-labelledby="maintenanceModalLabel">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="maintenanceModalLabel">
<i class="bi bi-wrench-adjustable me-2"></i>
<span id="modalTitleText">Add Maintenance Record</span>
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<form id="maintenanceForm" method="POST" action="{{ url_for('maintenance.create') }}">
<div class="modal-body">
<!-- Asset -->
<div class="mb-3">
<label class="form-label fw-semibold">Asset <span class="text-danger">*</span></label>
<select name="asset_id" id="fieldAsset" class="form-select" required>
<option value="">— select asset —</option>
{% for a in all_assets %}
<option value="{{ a.id }}">
{{ a.serial_number }}{% if a.brand %} {{ a.brand }}{% endif %}{% if a.model %} {{ a.model }}{% endif %}
</option>
{% endfor %}
</select>
</div>
<div class="row g-3 mb-3">
<!-- Start date -->
<div class="col-md-4">
<label class="form-label fw-semibold">Start Date <span class="text-danger">*</span></label>
<input type="date" name="start_date" id="fieldStart" class="form-control" required>
</div>
<!-- End date -->
<div class="col-md-4">
<label class="form-label fw-semibold">End Date</label>
<input type="date" name="end_date" id="fieldEnd" class="form-control">
</div>
<!-- Status -->
<div class="col-md-4">
<label class="form-label fw-semibold">Status</label>
<select name="status" id="fieldStatus" class="form-select">
{% for val, label in statuses %}
<option value="{{ val }}">{{ label }}</option>
{% endfor %}
</select>
</div>
</div>
<!-- Initial diagnosis -->
<div class="mb-3">
<label class="form-label fw-semibold">Initial Diagnosis</label>
<textarea name="initial_diagnosis" id="fieldDiagnosis" class="form-control" rows="3"
placeholder="Describe the fault or reason for maintenance…"></textarea>
</div>
<!-- Action taken -->
<div class="mb-3">
<label class="form-label fw-semibold">Action Taken</label>
<textarea name="action_taken" id="fieldAction" class="form-control" rows="3"
placeholder="Describe the work performed…"></textarea>
</div>
<!-- Finished action result -->
<div class="mb-3">
<label class="form-label fw-semibold">Finished Action Result</label>
<textarea name="action_result" id="fieldResult" class="form-control" rows="3"
placeholder="Outcome, parts replaced, verified status…"></textarea>
</div>
</div><!-- /.modal-body -->
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">
<i class="bi bi-floppy me-1"></i><span id="submitBtnText">Save Record</span>
</button>
</div>
</form>
</div>
</div>
</div>
{% endif %}
{% endblock %}
{% block extra_js %}
{% if current_user.is_editor %}
<script>
const modal = document.getElementById('maintenanceModal');
const form = document.getElementById('maintenanceForm');
const titleText = document.getElementById('modalTitleText');
const submitText = document.getElementById('submitBtnText');
const createUrl = "{{ url_for('maintenance.create') }}";
const _updateBase = "{{ url_for('maintenance.update', record_id=0) }}";
const updateUrl = (id) => _updateBase.replace('/0/', `/${id}/`);
// ── Open for EDIT when a table row is clicked ────────────────────────────────
document.querySelectorAll('tr[data-id]').forEach(row => {
row.addEventListener('click', () => {
const id = row.dataset.id;
titleText.textContent = 'Edit Maintenance Record';
submitText.textContent = 'Update Record';
form.action = updateUrl(id);
document.getElementById('fieldAsset').value = row.dataset.asset;
document.getElementById('fieldStart').value = row.dataset.start;
document.getElementById('fieldEnd').value = row.dataset.end;
document.getElementById('fieldStatus').value = row.dataset.status;
document.getElementById('fieldDiagnosis').value= row.dataset.diagnosis;
document.getElementById('fieldAction').value = row.dataset.action;
document.getElementById('fieldResult').value = row.dataset.result;
// Lock asset field in edit mode (asset cannot change)
document.getElementById('fieldAsset').disabled = true;
});
});
// ── Reset to ADD mode when modal is opened via the + button ─────────────────
document.getElementById('btnAddRecord').addEventListener('click', () => {
titleText.textContent = 'Add Maintenance Record';
submitText.textContent = 'Save Record';
form.action = createUrl;
form.reset();
document.getElementById('fieldAsset').disabled = false;
// Default start date to today
document.getElementById('fieldStart').value = new Date().toISOString().split('T')[0];
});
// ── Re-enable asset select before submit (disabled fields are not posted) ────
form.addEventListener('submit', () => {
document.getElementById('fieldAsset').disabled = false;
});
// ── If page loaded with edit_record (server-side edit link), auto-open modal ─
{% if edit_record %}
const editRow = document.querySelector('tr[data-id="{{ edit_record.id }}"]');
if (editRow) editRow.click();
else {
// Fallback: populate manually
titleText.textContent = 'Edit Maintenance Record';
submitText.textContent = 'Update Record';
form.action = updateUrl({{ edit_record.id }});
document.getElementById('fieldAsset').value = '{{ edit_record.asset_id }}';
document.getElementById('fieldStart').value = '{{ edit_record.start_date.isoformat() if edit_record.start_date else "" }}';
document.getElementById('fieldEnd').value = '{{ edit_record.end_date.isoformat() if edit_record.end_date else "" }}';
document.getElementById('fieldStatus').value = '{{ edit_record.status }}';
document.getElementById('fieldDiagnosis').value= {{ edit_record.initial_diagnosis | tojson }};
document.getElementById('fieldAction').value = {{ edit_record.action_taken | tojson }};
document.getElementById('fieldResult').value = {{ edit_record.action_result | tojson }};
document.getElementById('fieldAsset').disabled = true;
new bootstrap.Modal(document.getElementById('maintenanceModal')).show();
}
{% endif %}
</script>
{% endif %}
{% endblock %}