Lint / JS (eslint) (push) Successful in 7s
Lint / Python (flake8) (push) Successful in 49s
Lint / Notify on failure (push) Skipped
Lint / Deploy (push) Skipped
Security / Python Security (bandit) (push) Successful in 51s
Lint / Python (flake8) (pull_request) Successful in 55s
Test / Python Tests (pytest) (push) Successful in 1m4s
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 52s
Test / Python Tests (pytest) (pull_request) Successful in 55s
Items are mounted from the front, the rear, or full depth. On a 4-post rack a front and a rear item can share a unit; a full-depth one takes both sides, and the rear view shows its back. Overlaps are checked per side. Shelf contents take the shelf's side. A Front / Rear switch flips the drawing. Racks are saved in Gandalf (new cutsheet_racks table) and edited on the rack page by admins: name, height, notes, add and delete. Renaming moves the rack's items; saving the first rack carries over the items of the config default it replaces. Config racks remain the fallback. Faceplates are stretched to the full width only when the drawing is close to the slot's proportions, so the PDU is not squashed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
89 lines
4.3 KiB
HTML
89 lines
4.3 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='rack.js') }}"></script>
|
|
{% endblock %}
|