{% extends 'dcim/device/base.html' %} {% block content %} {% if ledger_enabled and has_mounts %} {% include 'netbox_cabinet_view/inc/slot_ledger.html' %} {% endif %}
Cabinet Layout
{% if has_bays %}
{% csrf_token %}
{% endif %} Add Mount
{% if has_mounts %} {% if has_face_specific %} {# Feature 1 (v0.5.0): dual-SVG rendering when mounts have explicit faces. #} {# v0.7.1: data-src instead of data — JS populates data with ?theme= appended. #}
Front
Cabinet layout (front) for {{ object }}
Rear
Cabinet layout (rear) for {{ object }}
{% else %} Cabinet layout for {{ object }} {% endif %} {% else %} {# ========================================================== #} {# Finding B (v0.4.0): empty-state scale-reference canvas. #} {# #} {# When the device has a profile but no mounts yet, render a #} {# dashed rectangle sized to the internal_width/height_mm so #} {# users see the cabinet's actual scale before they pick a #} {# mount type. Centered CTA button offers the "+ first mount" #} {# flow. If the profile has no internal dims, degrade to a #} {# plain card with just the CTA. #} {# ========================================================== #} {% if internal_width_mm and internal_height_mm %} {# 2 px per mm, matches CabinetLayoutSVG.DEFAULT_MM_TO_PX #} {% widthratio internal_width_mm 1 2 as canvas_w %} {% widthratio internal_height_mm 1 2 as canvas_h %}
No mounts defined
Interior: {{ internal_width_mm }} × {{ internal_height_mm }} mm
Add the first mount
{% else %}
No mounts defined for this device.
Add the first mount
{% endif %} {% endif %}
{% if has_mounts %} {# =================================================================== #} {# v0.7.1: propagate NetBox dark/light theme into -embedded #} {# SVGs via a server-side `?theme=dark|light` query param. The #} {# elements use `data-src` (not `data`) so the SVG never #} {# loads without the theme param. This script reads the active theme #} {# from localStorage/data-bs-theme and sets the real `data` attribute #} {# with `&theme=` appended, triggering the initial load. On #} {# theme toggle, it reloads all SVGs with the new theme. #} {# =================================================================== #} {# =================================================================== #} {# Finding C (v0.4.0): 2D click-anywhere handler for mounting plates. #} {# #} {# The SVG renderer emits one transparent `.slot.empty-slot.mount-2d` #} {# rect per 2D mount, tagged with data-mount-pk + data-mm-per-px + #} {# data-origin-x/y. On click, we read the pointer's offset inside that #} {# rect, convert to mm relative to the mount's origin, round to the #} {# nearest mm, and navigate to the PlacementForm with mount, position_x #} {# and position_y pre-filled. 1D and grid mounts already have per-slot #} {# hyperlinks; they don't need this JS. #} {# =================================================================== #} {# =================================================================== #} {# v0.7.0 Feature 2: drag-to-place. #} {# #} {# Populated placement rects carry data-placement-pk + mount geometry #} {# attributes. On drag, a ghost rect follows the cursor with grid snap. #} {# On drop, PATCH /api/plugins/netbox-cabinet-view/placements// #} {# with the new position. On success, reload the SVG via cache-buster. #} {# Click vs. drag distinguished by a 5px threshold — if the mouse #} {# doesn't move enough, we navigate to the device detail URL instead. #} {# =================================================================== #}
Mounts
{% for mount in mounts %} {% endfor %}
Name Type Subtype Unit Size Placements
{{ mount.name }} {{ mount.get_mount_type_display }} {{ mount.get_subtype_display|default:"—" }} {{ mount.get_unit_display }} {% if mount.length_mm %} {{ mount.length_mm }} mm ({{ mount.capacity_units }} units) {% else %} {{ mount.width_mm }} × {{ mount.height_mm }} mm {% endif %} {{ mount.placements.count }} Placement
{% endif %} {% endblock content %}