Add site panel

This commit is contained in:
Jeremy Stretch 2025-10-30 15:29:23 -04:00
parent 7d993cc141
commit 1acd567706
6 changed files with 72 additions and 90 deletions

View File

@ -44,3 +44,16 @@ class DeviceManagementPanel(ObjectPanel):
label=_('Out-of-band IP'),
template_name='dcim/device/attrs/ipaddress.html',
)
class SitePanel(ObjectPanel):
region = attrs.NestedObjectAttr('region', label=_('Region'), linkify=True)
group = attrs.NestedObjectAttr('group', label=_('Group'), linkify=True)
status = attrs.ChoiceAttr('status', label=_('Status'))
tenant = attrs.ObjectAttr('tenant', label=_('Tenant'), linkify=True, grouped_by='group')
facility = attrs.TextAttr('facility', label=_('Facility'))
description = attrs.TextAttr('description', label=_('Description'))
timezone = attrs.TimezoneAttr('time_zone', label=_('Timezone'))
physical_address = attrs.AddressAttr('physical_address', label=_('Physical address'), map_url=True)
shipping_address = attrs.AddressAttr('shipping_address', label=_('Shipping address'), map_url=True)
gps_coordinates = attrs.GPSCoordinatesAttr()

View File

@ -465,6 +465,7 @@ class SiteView(GetRelatedModelsMixin, generic.ObjectView):
def get_extra_context(self, request, instance):
return {
'site_panel': panels.SitePanel(instance, _('Site')),
'related_models': self.get_related_models(
request,
instance,

View File

@ -123,6 +123,30 @@ class NestedObjectAttr(Attr):
})
class AddressAttr(Attr):
template_name = 'components/attrs/address.html'
def __init__(self, *args, map_url=True, **kwargs):
super().__init__(*args, **kwargs)
if map_url is True:
self.map_url = get_config().MAPS_URL
elif map_url:
self.map_url = map_url
else:
self.map_url = None
def render(self, obj, context=None):
context = context or {}
value = self._resolve_attr(obj, self.accessor)
if value in (None, ''):
return self.placeholder
return render_to_string(self.template_name, {
**context,
'value': value,
'map_url': self.map_url,
})
class GPSCoordinatesAttr(Attr):
template_name = 'components/attrs/gps_coordinates.html'
@ -152,6 +176,20 @@ class GPSCoordinatesAttr(Attr):
})
class TimezoneAttr(Attr):
template_name = 'components/attrs/timezone.html'
def render(self, obj, context=None):
context = context or {}
value = self._resolve_attr(obj, self.accessor)
if value in (None, ''):
return self.placeholder
return render_to_string(self.template_name, {
**context,
'value': value,
})
class TemplatedAttr(Attr):
def __init__(self, *args, context=None, **kwargs):

View File

@ -0,0 +1,8 @@
{% load i18n %}
{% load l10n %}
<span>{{ value|linebreaksbr }}</span>
{% if map_url %}
<a href="{{ map_url }}{{ value }}" target="_blank" class="btn btn-primary btn-sm print-none">
<i class="mdi mdi-map-marker"></i> {% trans "Map" %}
</a>
{% endif %}

View File

@ -0,0 +1,6 @@
{% load i18n %}
{% load tz %}
<div>
{{ value }} ({% trans "UTC" %} {{ value|tzoffset }})<br />
<small class="text-muted">{% trans "Local time" %}: {% timezone value %}{% now 'Y-m-d H:i' %}{% endtimezone %}</small>
</div>

View File

@ -24,100 +24,16 @@
{% block content %}
<div class="row">
<div class="col col-12 col-md-6">
<div class="card">
<h2 class="card-header">{% trans "Site" %}</h2>
<table class="table table-hover attr-table">
<tr>
<th scope="row">{% trans "Region" %}</th>
<td>
{% nested_tree object.region %}
</td>
</tr>
<tr>
<th scope="row">{% trans "Group" %}</th>
<td>
{% nested_tree object.group %}
</td>
</tr>
<tr>
<th scope="row">{% trans "Status" %}</th>
<td>{% badge object.get_status_display bg_color=object.get_status_color %}</td>
</tr>
<tr>
<th scope="row">{% trans "Tenant" %}</th>
<td>
{% if object.tenant.group %}
{{ object.tenant.group|linkify }} /
{% endif %}
{{ object.tenant|linkify|placeholder }}
</td>
</tr>
<tr>
<th scope="row">{% trans "Facility" %}</th>
<td>{{ object.facility|placeholder }}</td>
</tr>
<tr>
<th scope="row">{% trans "Description" %}</th>
<td>{{ object.description|placeholder }}</td>
</tr>
<tr>
<th scope="row">{% trans "Time Zone" %}</th>
<td>
{% if object.time_zone %}
{{ object.time_zone }} ({% trans "UTC" %} {{ object.time_zone|tzoffset }})<br />
<small class="text-muted">{% trans "Site time" %}: {% timezone object.time_zone %}{% now 'Y-m-d H:i' %}{% endtimezone %}</small>
{% else %}
{{ ''|placeholder }}
{% endif %}
</td>
</tr>
<tr>
<th scope="row">{% trans "Physical Address" %}</th>
<td class="d-flex justify-content-between align-items-start">
{% if object.physical_address %}
<span>{{ object.physical_address|linebreaksbr }}</span>
{% if config.MAPS_URL %}
<a href="{{ config.MAPS_URL }}{{ object.physical_address|urlencode }}" target="_blank" class="btn btn-primary btn-sm d-print-none">
<i class="mdi mdi-map-marker"></i> {% trans "Map" %}
</a>
{% endif %}
{% else %}
{{ ''|placeholder }}
{% endif %}
</td>
</tr>
<tr>
<th scope="row">{% trans "Shipping Address" %}</th>
<td>{{ object.shipping_address|linebreaksbr|placeholder }}</td>
</tr>
<tr>
<th scope="row">{% trans "GPS Coordinates" %}</th>
<td class="position-relative">
{% if object.latitude and object.longitude %}
{% if config.MAPS_URL %}
<div class="position-absolute top-50 end-0 me-2 translate-middle-y d-print-none">
<a href="{{ config.MAPS_URL }}{{ object.latitude|unlocalize }},{{ object.longitude|unlocalize }}" target="_blank" class="btn btn-primary btn-sm">
<i class="mdi mdi-map-marker"></i> {% trans "Map" %}
</a>
</div>
{% endif %}
<span>{{ object.latitude }}, {{ object.longitude }}</span>
{% else %}
{{ ''|placeholder }}
{% endif %}
</td>
</tr>
</table>
</div>
{{ site_panel }}
{% include 'inc/panels/custom_fields.html' %}
{% include 'inc/panels/tags.html' %}
{% include 'inc/panels/comments.html' %}
{% plugin_left_page object %}
</div>
<div class="col col-12 col-md-6">
{% include 'inc/panels/related_objects.html' with filter_name='site_id' %}
{% include 'inc/panels/image_attachments.html' %}
{% plugin_right_page object %}
</div>
<div class="col col-12 col-md-6">
{% include 'inc/panels/related_objects.html' with filter_name='site_id' %}
{% include 'inc/panels/image_attachments.html' %}
{% plugin_right_page object %}
</div>
</div>
<div class="row">