Files

454 lines
19 KiB
HTML
Raw Permalink 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 %}{{ asset.serial_number }} IT Asset Management{% endblock %}
{% block breadcrumb %}
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.index') }}">Home</a></li>
<li class="breadcrumb-item"><a href="{{ url_for('assets.index') }}">Assets</a></li>
<li class="breadcrumb-item active">{{ asset.serial_number }}</li>
{% endblock %}
{% block content %}
<div class="page-header d-flex align-items-center justify-content-between mb-4">
<h1>
<i class="bi bi-laptop me-2"></i>{{ asset.brand or '' }} {{ asset.model or '' }}
<span class="badge badge-{{ asset.status }} fs-6 align-middle ms-2">{{ asset.status | title }}</span>
</h1>
<div class="d-flex gap-2">
<a href="{{ url_for('assets.edit', asset_id=asset.id) }}" class="btn btn-sm btn-outline-primary">
<i class="bi bi-pencil me-1"></i>Edit
</a>
{% if asset.status == 'available' %}
<a href="{{ url_for('assignments.create', asset_id=asset.id) }}" class="btn btn-sm btn-outline-success">
<i class="bi bi-plus-circle me-1"></i>Assign
</a>
{% endif %}
{% if asset.current_user %}
<a href="{{ url_for('paperwork.create', asset_id=asset.id, user_id=asset.current_user.id) }}"
class="btn btn-sm btn-outline-info">
<i class="bi bi-file-earmark-plus me-1"></i>New Doc
</a>
{% endif %}
</div>
</div>
<div class="row g-3">
<!-- Asset details -->
<div class="col-md-4">
<div class="card border-0 shadow-sm mb-3">
<div class="card-header bg-white fw-semibold py-3">
<i class="bi bi-info-circle me-2 text-primary"></i>Asset Details
</div>
<div class="card-body">
<dl class="row mb-0">
<dt class="col-5 text-muted small">Type</dt>
<dd class="col-7"><span class="badge bg-secondary">{{ asset.asset_type }}</span></dd>
<dt class="col-5 text-muted small">Brand</dt>
<dd class="col-7">{{ asset.brand or '—' }}</dd>
<dt class="col-5 text-muted small">Model</dt>
<dd class="col-7">{{ asset.model or '—' }}</dd>
<dt class="col-5 text-muted small">Serial No.</dt>
<dd class="col-7"><code>{{ asset.serial_number }}</code></dd>
<dt class="col-5 text-muted small">Service Tag</dt>
<dd class="col-7"><code>{{ asset.service_tag or '—' }}</code></dd>
<dt class="col-5 text-muted small">Asset Tag</dt>
<dd class="col-7">{{ asset.asset_tag or '—' }}</dd>
<dt class="col-5 text-muted small">OS</dt>
<dd class="col-7">{{ asset.operating_system or '—' }}</dd>
{% if asset.processor %}
<dt class="col-5 text-muted small">CPU</dt>
<dd class="col-7">{{ asset.processor }}</dd>
{% endif %}
{% if asset.ram_gb %}
<dt class="col-5 text-muted small">RAM</dt>
<dd class="col-7">{{ asset.ram_gb }} GB</dd>
{% endif %}
{% if asset.storage_gb %}
<dt class="col-5 text-muted small">Storage</dt>
<dd class="col-7">{{ asset.storage_gb }} GB</dd>
{% endif %}
{% if asset.mac_address %}
<dt class="col-5 text-muted small">MAC</dt>
<dd class="col-7"><code>{{ asset.mac_address }}</code></dd>
{% endif %}
</dl>
</div>
</div>
<div class="card border-0 shadow-sm">
<div class="card-header bg-white fw-semibold py-3">
<i class="bi bi-receipt me-2 text-primary"></i>Procurement
</div>
<div class="card-body">
<dl class="row mb-0">
<dt class="col-5 text-muted small">Purchased</dt>
<dd class="col-7">{{ asset.purchase_date.strftime('%d/%m/%Y') if asset.purchase_date else '—' }}</dd>
<dt class="col-5 text-muted small">Warranty</dt>
<dd class="col-7">{{ asset.warranty_expiry.strftime('%d/%m/%Y') if asset.warranty_expiry else '—' }}</dd>
<dt class="col-5 text-muted small">Price</dt>
<dd class="col-7">{{ '%.2f'|format(asset.purchase_price) if asset.purchase_price else '—' }}</dd>
<dt class="col-5 text-muted small">Supplier</dt>
<dd class="col-7">{{ asset.supplier or '—' }}</dd>
<dt class="col-5 text-muted small">PO #</dt>
<dd class="col-7">{{ asset.po_number or '—' }}</dd>
<dt class="col-5 text-muted small">Location</dt>
<dd class="col-7">{{ asset.location or '—' }}</dd>
</dl>
{% if asset.notes %}
<hr class="my-2">
<p class="small text-muted mb-0">{{ asset.notes }}</p>
{% endif %}
</div>
</div>
</div>
<!-- History + Docs -->
<div class="col-md-8">
<!-- Assignment history -->
<div class="card border-0 shadow-sm mb-3">
<div class="card-header bg-white fw-semibold py-3 d-flex justify-content-between align-items-center">
<span><i class="bi bi-clock-history me-2 text-primary"></i>Assignment History</span>
</div>
<div class="table-responsive">
<table class="table table-sm table-hover mb-0">
<thead class="table-light">
<tr>
<th>User</th>
<th>Windows ID</th>
<th>From</th>
<th>Until</th>
<th>Status</th>
<th></th>
</tr>
</thead>
<tbody>
{% for a in history %}
<tr {% if a.user.is_masked %}class="masked-row"{% endif %}>
<td>{{ a.user.display_name }}</td>
<td><code>{{ a.user.windows_id }}</code></td>
<td>{{ a.assigned_date.strftime('%d/%m/%Y') if a.assigned_date else '—' }}</td>
<td>{{ a.returned_date.strftime('%d/%m/%Y') if a.returned_date else '—' }}</td>
<td>
{% if a.is_active %}
<span class="badge bg-success">Active</span>
{% else %}
<span class="badge bg-secondary">Returned</span>
{% endif %}
</td>
<td>
{% if a.is_active %}
<button class="btn btn-sm btn-outline-warning py-0 px-2"
data-bs-toggle="modal" data-bs-target="#returnModal{{ a.id }}">
<i class="bi bi-arrow-return-left"></i> Return
</button>
{% endif %}
</td>
</tr>
{% else %}
<tr><td colspan="6" class="text-center text-muted py-3">No assignment history.</td></tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<!-- Documents -->
<div class="card border-0 shadow-sm">
<div class="card-header bg-white fw-semibold py-3 d-flex justify-content-between align-items-center">
<span><i class="bi bi-file-earmark-text me-2 text-primary"></i>Documents</span>
{% if asset.current_user %}
<a href="{{ url_for('paperwork.create', asset_id=asset.id, user_id=asset.current_user.id) }}"
class="btn btn-sm btn-outline-info py-0 px-2">
<i class="bi bi-plus"></i> New
</a>
{% endif %}
</div>
<div class="table-responsive">
<table class="table table-sm table-hover mb-0">
<thead class="table-light">
<tr><th>Title</th><th>Type</th><th>User</th><th>Date</th><th></th></tr>
</thead>
<tbody>
{% for d in docs %}
<tr>
<td><a href="{{ url_for('paperwork.detail', doc_id=d.id) }}">{{ d.title }}</a></td>
<td><span class="badge bg-info text-dark">{{ d.doc_type_label }}</span></td>
<td>{{ d.user.display_name }}</td>
<td>{{ d.created_at.strftime('%d/%m/%Y') if d.created_at else '—' }}</td>
<td>
{% if d.pdf_filename %}
<a href="{{ url_for('paperwork.download', doc_id=d.id) }}"
class="btn btn-sm btn-outline-secondary py-0 px-2">
<i class="bi bi-download"></i>
</a>
{% endif %}
</td>
</tr>
{% else %}
<tr><td colspan="5" class="text-center text-muted py-3">No documents.</td></tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
{% if asset.asset_type in ('Laptop', 'Desktop') %}
<!-- Compliance card — Laptop / Desktop only -->
<div class="card border-0 shadow-sm mt-3">
<div class="card-header bg-white fw-semibold py-3 d-flex justify-content-between align-items-center">
<span><i class="bi bi-shield-check me-2 text-success"></i>IT Compliance &amp; Inventory</span>
<button class="btn btn-sm btn-outline-primary" type="button"
data-bs-toggle="collapse" data-bs-target="#complianceEdit">
<i class="bi bi-pencil me-1"></i>Edit
</button>
</div>
<!-- Read-only summary -->
<div class="card-body pb-2">
<div class="row g-2">
<div class="col-md-4 col-6">
<div class="small text-muted">Inventory #</div>
<div class="fw-semibold">{{ asset.inventory_number or '—' }}</div>
</div>
<div class="col-md-4 col-6">
<div class="small text-muted">AD Device Name</div>
<div class="fw-semibold"><code>{{ asset.ad_device_name or '—' }}</code></div>
</div>
<div class="col-md-4 col-12">
<div class="small text-muted">Location Note</div>
<div class="fw-semibold">{{ asset.location_note or '—' }}</div>
</div>
<div class="col-md-4 col-4 mt-2">
{% if asset.encryption_checked %}
<span class="badge bg-success"><i class="bi bi-lock-fill me-1"></i>Encrypted</span>
{% else %}
<span class="badge bg-danger"><i class="bi bi-lock me-1"></i>Not Encrypted</span>
{% endif %}
{% if asset.encryption_checked_by %}
<div class="small text-muted mt-1">
by <strong>{{ asset.encryption_checked_by.username }}</strong>
{% if asset.encryption_checked_at %}
&mdash; {{ asset.encryption_checked_at.strftime('%d/%m/%Y %H:%M') }}
{% endif %}
</div>
{% endif %}
</div>
<div class="col-md-4 col-4 mt-2">
{% if asset.backup_checked %}
<span class="badge bg-success"><i class="bi bi-cloud-check me-1"></i>Backup OK</span>
{% else %}
<span class="badge bg-warning text-dark"><i class="bi bi-cloud me-1"></i>No Backup</span>
{% endif %}
{% if asset.backup_checked_by %}
<div class="small text-muted mt-1">
by <strong>{{ asset.backup_checked_by.username }}</strong>
{% if asset.backup_checked_at %}
&mdash; {{ asset.backup_checked_at.strftime('%d/%m/%Y %H:%M') }}
{% endif %}
</div>
{% endif %}
</div>
<div class="col-md-4 col-4 mt-2">
{% if asset.hr_notified %}
<span class="badge bg-success"><i class="bi bi-person-check me-1"></i>HR Notified</span>
{% else %}
<span class="badge bg-secondary"><i class="bi bi-person me-1"></i>HR Pending</span>
{% endif %}
{% if asset.hr_notified_by %}
<div class="small text-muted mt-1">
by <strong>{{ asset.hr_notified_by.username }}</strong>
{% if asset.hr_notified_at %}
&mdash; {{ asset.hr_notified_at.strftime('%d/%m/%Y %H:%M') }}
{% endif %}
</div>
{% endif %}
</div>
</div>
</div>
<!-- Collapsible edit form -->
<div class="collapse" id="complianceEdit">
<div class="card-body border-top pt-3">
<form method="POST" action="{{ url_for('assets.update_compliance', asset_id=asset.id) }}">
<div class="row g-3">
<div class="col-md-4">
<label class="form-label small fw-semibold">Inventory Number</label>
<input type="text" name="inventory_number" class="form-control form-control-sm"
value="{{ asset.inventory_number or '' }}" placeholder="INV-0001">
</div>
<div class="col-md-4">
<label class="form-label small fw-semibold">AD Device Name</label>
<input type="text" name="ad_device_name" class="form-control form-control-sm"
value="{{ asset.ad_device_name or '' }}" placeholder="DESKTOP-AB1234">
</div>
<div class="col-md-4">
<label class="form-label small fw-semibold">Current User in AD</label>
{% if asset.current_user %}
<div class="form-control form-control-sm bg-light text-muted">
{{ asset.current_user.display_name }} ({{ asset.current_user.windows_id }})
</div>
{% else %}
<div class="form-control form-control-sm bg-light text-muted">Not assigned</div>
{% endif %}
</div>
<div class="col-12">
<label class="form-label small fw-semibold">Location Note</label>
<textarea name="location_note" class="form-control form-control-sm" rows="2"
placeholder="e.g. Building A, Room 102, Desk 5">{{ asset.location_note or '' }}</textarea>
</div>
<div class="col-12">
<div class="d-flex gap-4 flex-wrap mt-1">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" name="encryption_checked"
id="chkEncrypt" value="1"
{% if asset.encryption_checked %}checked{% endif %}>
<label class="form-check-label fw-semibold" for="chkEncrypt">
<i class="bi bi-lock-fill me-1 text-success"></i>Encryption Verified
</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" name="backup_checked"
id="chkBackup" value="1"
{% if asset.backup_checked %}checked{% endif %}>
<label class="form-check-label fw-semibold" for="chkBackup">
<i class="bi bi-cloud-check me-1 text-primary"></i>Backup Configured
</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" name="hr_notified"
id="chkHR" value="1"
{% if asset.hr_notified %}checked{% endif %}>
<label class="form-check-label fw-semibold" for="chkHR">
<i class="bi bi-person-check me-1 text-warning"></i>HR Send / Notified
</label>
</div>
</div>
</div>
<div class="col-12">
<label class="form-label small fw-semibold" for="compliance_notes">
<i class="bi bi-chat-left-text me-1 text-secondary"></i>Note
<span class="text-muted fw-normal">(reason for check / uncheck — saved with each change)</span>
</label>
<textarea name="compliance_notes" id="compliance_notes"
class="form-control form-control-sm" rows="2"
placeholder="e.g. BitLocker verified on site visit, backup re-enabled after restore…"></textarea>
</div>
</div>
<div class="d-flex gap-2 mt-3">
<button type="submit" class="btn btn-sm btn-success">
<i class="bi bi-check2 me-1"></i>Save Changes
</button>
<button type="button" class="btn btn-sm btn-outline-secondary"
data-bs-toggle="collapse" data-bs-target="#complianceEdit">
Cancel
</button>
</div>
</form>
</div>
</div>
</div>
<!-- Compliance change history -->
{% if compliance_log %}
<div class="card border-0 shadow-sm mt-2">
<div class="card-header bg-white fw-semibold py-3 d-flex justify-content-between align-items-center">
<!-- Compliance per-check history -->
{% if check_history %}
<div class="card border-0 shadow-sm mt-2">
<div class="card-header bg-white fw-semibold py-3 d-flex justify-content-between align-items-center">
<span><i class="bi bi-shield-exclamation me-2 text-secondary"></i>Compliance Check History</span>
<button class="btn btn-sm btn-outline-secondary" type="button"
data-bs-toggle="collapse" data-bs-target="#checkHistory">
Show / Hide
</button>
</div>
<div class="collapse" id="checkHistory">
<div class="table-responsive">
<table class="table table-sm table-hover mb-0 small">
<thead class="table-light">
<tr>
<th style="width:160px">Date &amp; Time</th>
<th style="width:140px">Check</th>
<th style="width:90px">Result</th>
<th style="width:130px">Performed by</th>
<th>Note</th>
</tr>
</thead>
<tbody>
{% for entry in check_history %}
<tr>
<td class="text-nowrap">{{ entry.performed_at.strftime('%d/%m/%Y %H:%M') }}</td>
<td>{{ entry.check_type_label }}</td>
<td>
{% if entry.checked %}
<span class="badge bg-success">Verified</span>
{% else %}
<span class="badge bg-danger">Cleared</span>
{% endif %}
</td>
<td>
{% if entry.performed_by %}
<span class="fw-semibold">{{ entry.performed_by.username }}</span>
{% else %}
<span class="text-muted"></span>
{% endif %}
</td>
<td>{{ entry.notes or '—' }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endif %}
{% endif %}{# end asset_type in Laptop/Desktop #}
<!-- Return modals -->
{% for a in history %}{% if a.is_active %}
<div class="modal fade" id="returnModal{{ a.id }}" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Return Asset</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<form method="POST" action="{{ url_for('assignments.return_asset', assignment_id=a.id) }}">
<div class="modal-body">
<div class="mb-3">
<label class="form-label">Return Date</label>
<input type="date" name="returned_date" class="form-control"
value="{{ today_date }}" required>
</div>
<div class="mb-3">
<label class="form-label">Notes (optional)</label>
<textarea name="return_notes" class="form-control" rows="2"></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-warning">Confirm Return</button>
</div>
</form>
</div>
</div>
</div>
{% endif %}{% endfor %}
{% endblock %}