{% load i18n %} {% load static %} {% load rack_design %} {# ------------------------------------------------------------------------- #} {# Shared per-rack block: one rack rendered with BOTH its Front and Rear #} {# faces side by side, full-depth opposite-face hatch and the hover-card #} {# data-* attributes. Used by BOTH the interactive editor #} {# (design_editor.html, editable=True) and the read-only projected elevation #} {# (design_elevation.html, editable=False) so the two can never drift apart. #} {# #} {# Context: #} {# b the per-rack bundle from views._project_rack_bundle: #} {# {rack, front, rear, non_racked, widgets, rack_meta, hidden?} #} {# editable True -> editor affordances (drag grids, per-tile × remove, #} {# embedded widget JSON payload for editor.js). #} {# False -> static, read-only (locked tiles, no remove buttons, #} {# no JSON payload). View-only Front/Rear toggles + the #} {# hover card still work. #} {# #} {# Widget indices (editor only): the page builds `widgets` in the order #} {# (*front, *rear, *non_racked); each tile is stamped with its GLOBAL index #} {# via gs-id/data-widget-index so editor.js can pair JSON to DOM. #} {# ------------------------------------------------------------------------- #} {% with rack=b.rack front=b.front rear=b.rear non_racked=b.non_racked %} {% with front_len=front|length rear_len=rear|length %} {% with rid=rack.pk|stringformat:"s" %}
{# ---- Per-rack header: name + site/location + face toggles ---- #}
{{ rack.name }} {% if rack.location %}· {{ rack.location }} {% elif rack.site %}· {{ rack.site }}{% endif %}
{# Independent on/off toggles per rack: each shows/hides its OWN face. #} {# Both start active; the JS refuses to hide the last visible face. #}
{# ---- Rack faces: Front + Rear side by side (independent toggles). ---- #}
{# ---- Front face ---- #}

{% trans "Front" %}

    {% for u in rack.units %}
  • {% if u|rack_whole_unit %}{{ u|floatformat:"0" }}{% endif %}
  • {% endfor %}
{# Positioning anchor for the displacement stripe bar (spec §3, user #} {# ruling 2026-07-09): the bar hangs OUTSIDE the grid's right edge, #} {# absolutely positioned against this wrapper. Created by editor.js #} {# only; the read-only elevation never renders one. #}
{% for slot in front %} {# A DISPLACED vacating slot (spec §3, ruling 2026-07-09) renders #} {# as the OUTSIDE stripe bar (after the grid, below), never a full #} {# tile composited under the occupant. Editable mode keeps the #} {# tile: editor.js collapses it and owns the bar (displaceOne). #} {% if editable or not slot.displaced %}
{% if editable and not slot.opposite_face %}{% endif %} {{ slot.label }}
{% endif %} {% endfor %}
{# Read-only displaced treatment (spec §3, ruling 2026-07-09): the #} {# outside stripe bar per displaced slot -- same geometry/CSS as the #} {# editor's makeStripeBar. Editable mode: editor.js owns the bars. #} {% if not editable %}{% for slot in front %}{% if slot.displaced %}
{% endif %}{% endfor %}{% endif %}
{# /.nbx-rd-grid-wrap #}
{# ---- Rear face ---- #}

{% trans "Rear" %}

    {% for u in rack.units %}
  • {% if u|rack_whole_unit %}{{ u|floatformat:"0" }}{% endif %}
  • {% endfor %}
{# Stripe-bar anchor, same as the front face above. #}
{% for slot in rear %} {% if editable or not slot.displaced %}
{% if editable and not slot.opposite_face %}{% endif %} {{ slot.label }}
{% endif %} {% endfor %}
{# Read-only displaced stripe bars, same as the front face above. #} {% if not editable %}{% for slot in rear %}{% if slot.displaced %}
{% endif %}{% endfor %}{% endif %}
{# /.nbx-rd-grid-wrap #}
{# /.nbx-rd-faces #} {# ---- Per-rack non-racked tray ---- #} {# Editor: always shown as a drop target. Read-only: shown only when there #} {# are non-racked placements (no empty drop affordance in view mode). #} {% if editable or non_racked %}

{% trans "Non-racked tray" %}

{% for slot in non_racked %} {# The tray is a LIST (spec §9.2/§9.4), not a grid with meaningful #} {# rows: every item gets its own sequential row (index * 2, since #} {# every tray tile is a fixed gs-h="2") so items never overlap and #} {# the container grows to fit -- server-render order first, session #} {# drops append after (see editor.js's tray "dropped" handler). #}
{% if editable %}{% endif %} {{ slot.label }}
{% endfor %}
{% endif %} {# Per-rack editor payload (editor only): one widget per projected slot, #} {# keyed by the rack pk so editor.js can hydrate each block independently. #} {% if editable %}{% with eid="rd-editor-data-"|add:rid %}{{ b.widgets|json_script:eid }}{% endwith %}{% endif %}
{# /.nbx-rd-rack-block #} {% endwith %}{% endwith %}{% endwith %}