Lint / JS (eslint) (push) Successful in 15s
Security / Python Security (bandit) (push) Successful in 1m5s
Lint / Python (flake8) (push) Successful in 49s
Lint / Notify on failure (push) Skipped
Lint / Deploy (push) Skipped
Lint / Python (flake8) (pull_request) Successful in 53s
Test / Python Tests (pytest) (push) Successful in 1m0s
Lint / JS (eslint) (pull_request) Successful in 7s
Lint / Notify on failure (pull_request) Skipped
Lint / Deploy (pull_request) Skipped
Security / Python Security (bandit) (pull_request) Successful in 53s
Test / Python Tests (pytest) (pull_request) Successful in 57s
Items Gandalf has no live faceplate for can pick a Front drawing: the UniFi NVR Pro (drawn from the CC0 NetBox devicetype-library image), the Sliger CX4712 4U and Rosewill RSV-L4500U 4U cases (from driveAtlas's layouts), and generic 1U/2U/4U servers. The power light shows the linked device's live status. They export with the rest of the rack. New cutsheet_items column chassis, added on first use. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
90 lines
4.4 KiB
HTML
90 lines
4.4 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}Rack{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="lt-page-header">
|
|
<div>
|
|
<h1 class="lt-page-title">Rack</h1>
|
|
<p class="g-page-sub">The rack front, drawn from the rack units on the cutsheet with live port state. Hover a port to light up every port on the same cable run; click it to open the port or the cable.</p>
|
|
</div>
|
|
<div class="cs-actions no-print">
|
|
<button type="button" class="lt-btn lt-btn-secondary lt-btn-sm" data-export="svg">Download SVG</button>
|
|
<button type="button" class="lt-btn lt-btn-secondary lt-btn-sm" data-export="png">Download PNG</button>
|
|
{% if can_edit %}
|
|
<button type="button" class="lt-btn lt-btn-secondary lt-btn-sm" data-action="rack-settings">Rack settings</button>
|
|
<button type="button" class="lt-btn lt-btn-secondary lt-btn-sm" data-action="add-rack">+ Rack</button>
|
|
<a class="lt-btn lt-btn-secondary lt-btn-sm" href="/cutsheet#cs-items-section">Edit positions</a>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="rack-bar">
|
|
<div class="rack-tabs" id="rack-tabs" role="tablist" aria-label="Racks" hidden></div>
|
|
<div class="rack-face" role="group" aria-label="Rack side">
|
|
<button type="button" class="rack-face-btn active" data-face="front" aria-pressed="true">Front</button>
|
|
<button type="button" class="rack-face-btn" data-face="rear" aria-pressed="false">Rear</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="rack-warnings" id="rack-warnings" role="alert" hidden></div>
|
|
|
|
<section class="g-section">
|
|
<div class="g-section-header">
|
|
<h2 class="g-section-title" id="rack-title">Front</h2>
|
|
<span class="g-section-badge" id="rack-count">0</span>
|
|
</div>
|
|
<p class="rack-readout" id="rack-readout" aria-live="polite"></p>
|
|
<div class="rack-legend" aria-hidden="true">
|
|
<span><i class="rack-key rack-key-ok"></i>matches cutsheet</span>
|
|
<span><i class="rack-key rack-key-bad"></i>mismatch</span>
|
|
<span><i class="rack-key rack-key-doc"></i>documented, unconfirmed</span>
|
|
<label class="rack-toggle"><input type="checkbox" id="rack-show-empty"> Show empty units</label>
|
|
</div>
|
|
<div class="rack" id="rack"><div class="link-loading">Loading rack</div></div>
|
|
<p class="cs-hint rack-unplaced" id="rack-unplaced"></p>
|
|
</section>
|
|
{% if can_edit %}
|
|
<div id="rack-modal" class="lt-modal-overlay" role="dialog" aria-modal="true" aria-labelledby="rack-modal-title" aria-hidden="true">
|
|
<div class="lt-modal">
|
|
<div class="lt-modal-header">
|
|
<h3 class="lt-modal-title" id="rack-modal-title">Rack settings</h3>
|
|
<button type="button" class="lt-modal-close" data-modal-close aria-label="Close">✕</button>
|
|
</div>
|
|
<form id="rack-form" autocomplete="off">
|
|
<div class="lt-modal-body">
|
|
<input type="hidden" name="id">
|
|
<input type="hidden" name="previous">
|
|
<div class="form-row">
|
|
<div class="lt-form-group">
|
|
<label class="lt-label" for="rack-name">Name <span class="required">*</span></label>
|
|
<input class="lt-input" id="rack-name" name="name" required maxlength="100" placeholder="e.g. StarTech 25U">
|
|
</div>
|
|
<div class="lt-form-group">
|
|
<label class="lt-label" for="rack-units">Height (U) <span class="required">*</span></label>
|
|
<input class="lt-input" id="rack-units" name="units" type="number" min="1" max="60" required>
|
|
</div>
|
|
</div>
|
|
<div class="lt-form-group">
|
|
<label class="lt-label" for="rack-notes">Notes</label>
|
|
<textarea class="lt-input" id="rack-notes" name="notes" rows="2" maxlength="2000" placeholder="model, location, depth"></textarea>
|
|
</div>
|
|
<p class="cs-hint">Renaming a rack moves its items with it. A rack can be deleted once it is empty.</p>
|
|
</div>
|
|
<div class="lt-modal-footer">
|
|
<button type="button" class="lt-btn lt-btn-danger" data-action="delete-rack">Delete</button>
|
|
<button type="button" class="lt-btn lt-btn-secondary" data-modal-close>Cancel</button>
|
|
<button type="submit" class="lt-btn lt-btn-primary">Save</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
{% endblock %}
|
|
|
|
{% block scripts %}
|
|
<script>const RACK_CAN_EDIT = {{ 'true' if can_edit else 'false' }};</script>
|
|
<script src="{{ url_for('static', filename='faceplates.js') }}"></script>
|
|
<script src="{{ url_for('static', filename='fronts.js') }}"></script>
|
|
<script src="{{ url_for('static', filename='rack.js') }}"></script>
|
|
{% endblock %}
|