diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index b4096973a..a1d1102de 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -797,41 +797,49 @@ class DeviceTypeView(generic.ObjectView): class DeviceTypeConsolePortsView(DeviceTypeComponentsView): child_model = ConsolePortTemplate table = tables.ConsolePortTemplateTable + filterset = filtersets.ConsolePortTemplateFilterSet class DeviceTypeConsoleServerPortsView(DeviceTypeComponentsView): child_model = ConsoleServerPortTemplate table = tables.ConsoleServerPortTemplateTable + filterset = filtersets.ConsoleServerPortTemplateFilterSet class DeviceTypePowerPortsView(DeviceTypeComponentsView): child_model = PowerPortTemplate table = tables.PowerPortTemplateTable + filterset = filtersets.PowerPortTemplateFilterSet class DeviceTypePowerOutletsView(DeviceTypeComponentsView): child_model = PowerOutletTemplate table = tables.PowerOutletTemplateTable + filterset = filtersets.PowerOutletTemplateFilterSet class DeviceTypeInterfacesView(DeviceTypeComponentsView): child_model = InterfaceTemplate table = tables.InterfaceTemplateTable + filterset = filtersets.InterfaceTemplateFilterSet class DeviceTypeFrontPortsView(DeviceTypeComponentsView): child_model = FrontPortTemplate table = tables.FrontPortTemplateTable + filterset = filtersets.FrontPortTemplateFilterSet class DeviceTypeRearPortsView(DeviceTypeComponentsView): child_model = RearPortTemplate table = tables.RearPortTemplateTable + filterset = filtersets.RearPortTemplateFilterSet class DeviceTypeDeviceBaysView(DeviceTypeComponentsView): child_model = DeviceBayTemplate table = tables.DeviceBayTemplateTable + filterset = filtersets.DeviceBayTemplateFilterSet class DeviceTypeEditView(generic.ObjectEditView): @@ -1328,30 +1336,35 @@ class DeviceView(generic.ObjectView): class DeviceConsolePortsView(DeviceComponentsView): child_model = ConsolePort table = tables.DeviceConsolePortTable + filterset = filtersets.ConsolePortFilterSet template_name = 'dcim/device/consoleports.html' class DeviceConsoleServerPortsView(DeviceComponentsView): child_model = ConsoleServerPort table = tables.DeviceConsoleServerPortTable + filterset = filtersets.ConsoleServerPortFilterSet template_name = 'dcim/device/consoleserverports.html' class DevicePowerPortsView(DeviceComponentsView): child_model = PowerPort table = tables.DevicePowerPortTable + filterset = filtersets.PowerPortFilterSet template_name = 'dcim/device/powerports.html' class DevicePowerOutletsView(DeviceComponentsView): child_model = PowerOutlet table = tables.DevicePowerOutletTable + filterset = filtersets.PowerOutletFilterSet template_name = 'dcim/device/poweroutlets.html' class DeviceInterfacesView(DeviceComponentsView): child_model = Interface table = tables.DeviceInterfaceTable + filterset = filtersets.InterfaceFilterSet template_name = 'dcim/device/interfaces.html' def get_children(self, request, parent): @@ -1364,24 +1377,28 @@ class DeviceInterfacesView(DeviceComponentsView): class DeviceFrontPortsView(DeviceComponentsView): child_model = FrontPort table = tables.DeviceFrontPortTable + filterset = filtersets.FrontPortFilterSet template_name = 'dcim/device/frontports.html' class DeviceRearPortsView(DeviceComponentsView): child_model = RearPort table = tables.DeviceRearPortTable + filterset = filtersets.RearPortFilterSet template_name = 'dcim/device/rearports.html' class DeviceDeviceBaysView(DeviceComponentsView): child_model = DeviceBay table = tables.DeviceDeviceBayTable + filterset = filtersets.DeviceBayFilterSet template_name = 'dcim/device/devicebays.html' class DeviceInventoryView(DeviceComponentsView): child_model = InventoryItem table = tables.DeviceInventoryItemTable + filterset = filtersets.InventoryItemFilterSet template_name = 'dcim/device/inventory.html' diff --git a/netbox/ipam/models/ip.py b/netbox/ipam/models/ip.py index c361acd01..aeb71e70f 100644 --- a/netbox/ipam/models/ip.py +++ b/netbox/ipam/models/ip.py @@ -195,6 +195,12 @@ class Aggregate(PrimaryModel): return self.prefix.version return None + def get_child_prefixes(self): + """ + Return all Prefixes within this Aggregate + """ + return Prefix.objects.filter(prefix__net_contained=str(self.prefix)) + def get_utilization(self): """ Determine the prefix utilization of the aggregate and return it as a percentage. diff --git a/netbox/ipam/urls.py b/netbox/ipam/urls.py index 541acb3ac..e9bba8fa1 100644 --- a/netbox/ipam/urls.py +++ b/netbox/ipam/urls.py @@ -61,6 +61,7 @@ urlpatterns = [ path('aggregates/edit/', views.AggregateBulkEditView.as_view(), name='aggregate_bulk_edit'), path('aggregates/delete/', views.AggregateBulkDeleteView.as_view(), name='aggregate_bulk_delete'), path('aggregates//', views.AggregateView.as_view(), name='aggregate'), + path('aggregates//prefixes/', views.AggregatePrefixesView.as_view(), name='aggregate_prefixes'), path('aggregates//edit/', views.AggregateEditView.as_view(), name='aggregate_edit'), path('aggregates//delete/', views.AggregateDeleteView.as_view(), name='aggregate_delete'), path('aggregates//changelog/', ObjectChangeLogView.as_view(), name='aggregate_changelog', kwargs={'model': Aggregate}), diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index 08253c054..a2942ea63 100644 --- a/netbox/ipam/views.py +++ b/netbox/ipam/views.py @@ -1,21 +1,22 @@ from django.contrib.contenttypes.models import ContentType from django.db.models import Prefetch from django.db.models.expressions import RawSQL -from django.http import Http404 from django.shortcuts import get_object_or_404, redirect, render from django.urls import reverse +from dcim.filtersets import InterfaceFilterSet from dcim.models import Device, Interface, Site from dcim.tables import SiteTable from netbox.views import generic from utilities.tables import paginate_table from utilities.utils import count_related +from virtualization.filtersets import VMInterfaceFilterSet from virtualization.models import VirtualMachine, VMInterface from . import filtersets, forms, tables from .constants import * from .models import * from .models import ASN -from .utils import add_available_ipaddresses, add_requested_prefixes, add_available_vlans +from .utils import add_requested_prefixes, add_available_vlans # @@ -274,39 +275,32 @@ class AggregateListView(generic.ObjectListView): class AggregateView(generic.ObjectView): queryset = Aggregate.objects.all() - def get_extra_context(self, request, instance): - # Find all child prefixes contained in this aggregate - prefix_list = Prefix.objects.restrict(request.user, 'view').filter( - prefix__net_contained_or_equal=str(instance.prefix) - ).prefetch_related( - 'site', 'role' - ).order_by( - 'prefix' - ) - # Return List of requested Prefixes +class AggregatePrefixesView(generic.ObjectChildrenView): + queryset = Aggregate.objects.all() + child_model = Prefix + table = tables.PrefixTable + filterset = filtersets.PrefixFilterSet + template_name = 'ipam/aggregate/prefixes.html' + + def get_children(self, request, parent): + return Prefix.objects.restrict(request.user, 'view').filter( + prefix__net_contained_or_equal=str(parent.prefix) + ).prefetch_related('site', 'role', 'tenant', 'vlan') + + def prep_table_data(self, request, queryset, parent): + # Determine whether to show assigned prefixes, available prefixes, or both show_available = bool(request.GET.get('show_available', 'true') == 'true') show_assigned = bool(request.GET.get('show_assigned', 'true') == 'true') - child_prefixes = add_requested_prefixes(instance.prefix, prefix_list, show_available, show_assigned) - prefix_table = tables.PrefixTable(child_prefixes, exclude=('utilization',)) - if request.user.has_perm('ipam.change_prefix') or request.user.has_perm('ipam.delete_prefix'): - prefix_table.columns.show('pk') - paginate_table(prefix_table, request) - - # Compile permissions list for rendering the object table - permissions = { - 'add': request.user.has_perm('ipam.add_prefix'), - 'change': request.user.has_perm('ipam.change_prefix'), - 'delete': request.user.has_perm('ipam.delete_prefix'), - } + return add_requested_prefixes(parent.prefix, queryset, show_available, show_assigned) + def get_extra_context(self, request, instance): return { - 'prefix_table': prefix_table, - 'permissions': permissions, 'bulk_querystring': f'within={instance.prefix}', - 'show_available': show_available, - 'show_assigned': show_assigned, + 'active_tab': 'prefixes', + 'show_available': bool(request.GET.get('show_available', 'true') == 'true'), + 'show_assigned': bool(request.GET.get('show_assigned', 'true') == 'true'), } @@ -457,17 +451,18 @@ class PrefixPrefixesView(generic.ObjectChildrenView): queryset = Prefix.objects.all() child_model = Prefix table = tables.PrefixTable + filterset = filtersets.PrefixFilterSet template_name = 'ipam/prefix/prefixes.html' def get_children(self, request, parent): - child_prefixes = parent.get_child_prefixes().restrict(request.user, 'view') + return parent.get_child_prefixes().restrict(request.user, 'view') - # Add available prefixes if requested + def prep_table_data(self, request, queryset, parent): + # Determine whether to show assigned prefixes, available prefixes, or both show_available = bool(request.GET.get('show_available', 'true') == 'true') show_assigned = bool(request.GET.get('show_assigned', 'true') == 'true') - child_prefixes = add_requested_prefixes(parent.prefix, child_prefixes, show_available, show_assigned) - return child_prefixes + return add_requested_prefixes(parent.prefix, queryset, show_available, show_assigned) def get_extra_context(self, request, instance): return { @@ -483,6 +478,7 @@ class PrefixIPRangesView(generic.ObjectChildrenView): queryset = Prefix.objects.all() child_model = IPRange table = tables.IPRangeTable + filterset = filtersets.IPRangeFilterSet template_name = 'ipam/prefix/ip_ranges.html' def get_children(self, request, parent): @@ -499,6 +495,7 @@ class PrefixIPAddressesView(generic.ObjectChildrenView): queryset = Prefix.objects.all() child_model = IPAddress table = tables.IPAddressTable + filterset = filtersets.IPAddressFilterSet template_name = 'ipam/prefix/ip_addresses.html' def get_children(self, request, parent): @@ -560,6 +557,7 @@ class IPRangeIPAddressesView(generic.ObjectChildrenView): queryset = IPRange.objects.all() child_model = IPAddress table = tables.IPAddressTable + filterset = filtersets.IPAddressFilterSet template_name = 'ipam/iprange/ip_addresses.html' def get_children(self, request, parent): @@ -959,6 +957,7 @@ class VLANInterfacesView(generic.ObjectChildrenView): queryset = VLAN.objects.all() child_model = Interface table = tables.VLANDevicesTable + filterset = InterfaceFilterSet template_name = 'ipam/vlan/interfaces.html' def get_children(self, request, parent): @@ -974,6 +973,7 @@ class VLANVMInterfacesView(generic.ObjectChildrenView): queryset = VLAN.objects.all() child_model = VMInterface table = tables.VLANVirtualMachinesTable + filterset = VMInterfaceFilterSet template_name = 'ipam/vlan/vminterfaces.html' def get_children(self, request, parent): diff --git a/netbox/netbox/views/generic.py b/netbox/netbox/views/generic.py index b27d4ee17..2c63ee978 100644 --- a/netbox/netbox/views/generic.py +++ b/netbox/netbox/views/generic.py @@ -23,6 +23,7 @@ from utilities.exceptions import AbortTransaction, PermissionsViolation from utilities.forms import ( BootstrapMixin, BulkRenameForm, ConfirmationForm, CSVDataField, CSVFileField, ImportForm, restrict_form_fields, ) +from utilities.htmx import is_htmx from utilities.permissions import get_permission_for_model from utilities.tables import paginate_table from utilities.utils import normalize_querydict, prepare_cloned_fields @@ -83,17 +84,28 @@ class ObjectChildrenView(ObjectView): queryset = None child_model = None table = None + filterset = None template_name = None def get_children(self, request, parent): """ - Return a QuerySet or iterable of child objects. + Return a QuerySet of child objects. request: The current request parent: The parent object """ raise NotImplementedError(f'{self.__class__.__name__} must implement get_children()') + def prep_table_data(self, request, queryset, parent): + """ + Provides a hook for subclassed views to modify data before initializing the table. + + :param request: The current request + :param queryset: The filtered queryset of child objects + :param parent: The parent object + """ + return queryset + def get(self, request, *args, **kwargs): """ GET handler for rendering child objects. @@ -101,17 +113,27 @@ class ObjectChildrenView(ObjectView): instance = get_object_or_404(self.queryset, **kwargs) child_objects = self.get_children(request, instance) + if self.filterset: + child_objects = self.filterset(request.GET, child_objects).qs + permissions = {} for action in ('change', 'delete'): perm_name = get_permission_for_model(self.child_model, action) permissions[action] = request.user.has_perm(perm_name) - table = self.table(child_objects, user=request.user) + table = self.table(self.prep_table_data(request, child_objects, instance), user=request.user) # Determine whether to display bulk action checkboxes if 'pk' in table.base_columns and (permissions['change'] or permissions['delete']): table.columns.show('pk') paginate_table(table, request) + # If this is an HTMX request, return only the rendered table HTML + if is_htmx(request): + return render(request, 'htmx/table.html', { + 'object': instance, + 'table': table, + }) + return render(request, self.get_template_name(), { 'object': instance, 'table': table, @@ -233,6 +255,12 @@ class ObjectListView(ObjectPermissionRequiredMixin, View): table = self.get_table(request, permissions) paginate_table(table, request) + # If this is an HTMX request, return only the rendered table HTML + if is_htmx(request): + return render(request, 'htmx/table.html', { + 'table': table, + }) + context = { 'content_type': content_type, 'table': table, diff --git a/netbox/project-static/dist/netbox-dark.css b/netbox/project-static/dist/netbox-dark.css index 4def5c73e..25017505e 100644 Binary files a/netbox/project-static/dist/netbox-dark.css and b/netbox/project-static/dist/netbox-dark.css differ diff --git a/netbox/project-static/dist/netbox-light.css b/netbox/project-static/dist/netbox-light.css index fa8a82cc4..07ad0dba2 100644 Binary files a/netbox/project-static/dist/netbox-light.css and b/netbox/project-static/dist/netbox-light.css differ diff --git a/netbox/project-static/dist/netbox-print.css b/netbox/project-static/dist/netbox-print.css index 3fda1a026..a09f49222 100644 Binary files a/netbox/project-static/dist/netbox-print.css and b/netbox/project-static/dist/netbox-print.css differ diff --git a/netbox/project-static/dist/netbox.js b/netbox/project-static/dist/netbox.js index c4a8e802d..95fd99270 100644 Binary files a/netbox/project-static/dist/netbox.js and b/netbox/project-static/dist/netbox.js differ diff --git a/netbox/project-static/dist/netbox.js.map b/netbox/project-static/dist/netbox.js.map index b95e6ba02..6fbe0874b 100644 Binary files a/netbox/project-static/dist/netbox.js.map and b/netbox/project-static/dist/netbox.js.map differ diff --git a/netbox/project-static/package.json b/netbox/project-static/package.json index 6b046c8fc..4ec08d7b3 100644 --- a/netbox/project-static/package.json +++ b/netbox/project-static/package.json @@ -30,6 +30,7 @@ "cookie": "^0.4.1", "dayjs": "^1.10.4", "flatpickr": "4.6.3", + "htmx.org": "^1.6.1", "just-debounce-it": "^1.4.0", "masonry-layout": "^4.2.2", "query-string": "^6.14.1", diff --git a/netbox/project-static/src/buttons/index.ts b/netbox/project-static/src/buttons/index.ts index 1fcc7b87e..251e0feaf 100644 --- a/netbox/project-static/src/buttons/index.ts +++ b/netbox/project-static/src/buttons/index.ts @@ -1,7 +1,6 @@ import { initConnectionToggle } from './connectionToggle'; import { initDepthToggle } from './depthToggle'; import { initMoveButtons } from './moveOptions'; -import { initPerPage } from './pagination'; import { initPreferenceUpdate } from './preferences'; import { initReslug } from './reslug'; import { initSelectAll } from './selectAll'; @@ -13,7 +12,6 @@ export function initButtons(): void { initReslug, initSelectAll, initPreferenceUpdate, - initPerPage, initMoveButtons, ]) { func(); diff --git a/netbox/project-static/src/buttons/pagination.ts b/netbox/project-static/src/buttons/pagination.ts deleted file mode 100644 index 670dc7390..000000000 --- a/netbox/project-static/src/buttons/pagination.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { getElements } from '../util'; - -function handlePerPageSelect(event: Event): void { - const select = event.currentTarget as HTMLSelectElement; - if (select.form !== null) { - select.form.submit(); - } -} - -export function initPerPage(): void { - for (const element of getElements('select.per-page')) { - element.addEventListener('change', handlePerPageSelect); - } -} diff --git a/netbox/project-static/src/index.ts b/netbox/project-static/src/index.ts index 30c25a406..266e69d6d 100644 --- a/netbox/project-static/src/index.ts +++ b/netbox/project-static/src/index.ts @@ -1,4 +1,5 @@ import '@popperjs/core'; import 'bootstrap'; +import 'htmx.org'; import 'simplebar'; import './netbox'; diff --git a/netbox/project-static/src/search.ts b/netbox/project-static/src/search.ts index 3140b8d7f..9e8a31c5b 100644 --- a/netbox/project-static/src/search.ts +++ b/netbox/project-static/src/search.ts @@ -1,5 +1,4 @@ -import debounce from 'just-debounce-it'; -import { getElements, getRowValues, findFirstAdjacent, isTruthy } from './util'; +import { getElements, findFirstAdjacent, isTruthy } from './util'; /** * Change the display value and hidden input values of the search filter based on dropdown @@ -41,109 +40,8 @@ function initSearchBar(): void { } } -/** - * Initialize Interface Table Filter Elements. - */ -function initInterfaceFilter(): void { - for (const input of getElements('input.interface-filter')) { - const table = findFirstAdjacent(input, 'table'); - const rows = Array.from( - table?.querySelectorAll('tbody > tr') ?? [], - ).filter(r => r !== null); - /** - * Filter on-page table by input text. - */ - function handleInput(event: Event): void { - const target = event.target as HTMLInputElement; - // Create a regex pattern from the input search text to match against. - const filter = new RegExp(target.value.toLowerCase().trim()); - - // Each row represents an interface and its attributes. - for (const row of rows) { - // Find the row's checkbox and deselect it, so that it is not accidentally included in form - // submissions. - const checkBox = row.querySelector('input[type="checkbox"][name="pk"]'); - if (checkBox !== null) { - checkBox.checked = false; - } - - // The data-name attribute's value contains the interface name. - const name = row.getAttribute('data-name'); - - if (typeof name === 'string') { - if (filter.test(name.toLowerCase().trim())) { - // If this row matches the search pattern, but is already hidden, unhide it. - if (row.classList.contains('d-none')) { - row.classList.remove('d-none'); - } - } else { - // If this row doesn't match the search pattern, hide it. - row.classList.add('d-none'); - } - } - } - } - input.addEventListener('keyup', debounce(handleInput, 300)); - } -} - -function initTableFilter(): void { - for (const input of getElements('input.object-filter')) { - // Find the first adjacent table element. - const table = findFirstAdjacent(input, 'table'); - - // Build a valid array of elements that are children of the adjacent table. - const rows = Array.from( - table?.querySelectorAll('tbody > tr') ?? [], - ).filter(r => r !== null); - - /** - * Filter table rows by matched input text. - * @param event - */ - function handleInput(event: Event): void { - const target = event.target as HTMLInputElement; - - // Create a regex pattern from the input search text to match against. - const filter = new RegExp(target.value.toLowerCase().trim()); - - // List of which rows which match the query - const matchedRows: Array = []; - - for (const row of rows) { - // Find the row's checkbox and deselect it, so that it is not accidentally included in form - // submissions. - const checkBox = row.querySelector('input[type="checkbox"][name="pk"]'); - if (checkBox !== null) { - checkBox.checked = false; - } - - // Iterate through each row's cell values - for (const value of getRowValues(row)) { - if (filter.test(value.toLowerCase())) { - // If this row matches the search pattern, add it to the list. - matchedRows.push(row); - break; - } - } - } - - // Iterate the rows again to set visibility. - // This results in a single reflow instead of one for each row. - for (const row of rows) { - if (matchedRows.indexOf(row) >= 0) { - row.classList.remove('d-none'); - } else { - row.classList.add('d-none'); - } - } - } - input.addEventListener('keyup', debounce(handleInput, 300)); - } -} - export function initSearch(): void { - for (const func of [initSearchBar, initTableFilter, initInterfaceFilter]) { + for (const func of [initSearchBar]) { func(); } } diff --git a/netbox/project-static/styles/netbox.scss b/netbox/project-static/styles/netbox.scss index 58bf18286..acbfa0646 100644 --- a/netbox/project-static/styles/netbox.scss +++ b/netbox/project-static/styles/netbox.scss @@ -737,10 +737,6 @@ nav.breadcrumb-container { } } -div.paginator > form > div.input-group { - width: fit-content; -} - label.required { font-weight: $font-weight-bold; @@ -900,14 +896,6 @@ div.card-overlay { } } -// Right-align the paginator element. -.paginator { - display: flex; - flex-direction: column; - align-items: flex-end; - padding: $spacer 0; -} - // Tabbed content .nav-tabs { .nav-link { diff --git a/netbox/project-static/yarn.lock b/netbox/project-static/yarn.lock index 9dfc0a3b3..780ba071e 100644 --- a/netbox/project-static/yarn.lock +++ b/netbox/project-static/yarn.lock @@ -1688,6 +1688,11 @@ hosted-git-info@^2.1.4, hosted-git-info@^2.8.9: resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== +htmx.org@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/htmx.org/-/htmx.org-1.6.1.tgz#6f0d59a93fa61cbaa15316c134a2f179045a5778" + integrity sha512-i+1k5ee2eFWaZbomjckyrDjUpa3FMDZWufatUSBmmsjXVksn89nsXvr1KLGIdAajiz+ZSL7TE4U/QaZVd2U2sA== + ignore@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" diff --git a/netbox/templates/dcim/connections_list.html b/netbox/templates/dcim/connections_list.html index 5dbea9129..ef8bef828 100644 --- a/netbox/templates/dcim/connections_list.html +++ b/netbox/templates/dcim/connections_list.html @@ -8,19 +8,14 @@ {% block content-wrapper %}
- {# Conncetions list #} + {# Connections list #}
- {% include 'inc/table_controls.html' %} - + {% include 'inc/table_controls_htmx.html' %}
-
-
- {% render_table table 'inc/table.html' %} -
+
+ {% include 'htmx/table.html' %}
- - {% include 'inc/paginator.html' with paginator=table.paginator page=table.page %}
{# Filter form #} diff --git a/netbox/templates/dcim/device/consoleports.html b/netbox/templates/dcim/device/consoleports.html index 066c19b73..b5a8a0f39 100644 --- a/netbox/templates/dcim/device/consoleports.html +++ b/netbox/templates/dcim/device/consoleports.html @@ -6,10 +6,14 @@ {% block content %}
{% csrf_token %} - {% include 'inc/table_controls.html' with table_modal="DeviceConsolePortTable_config" %} -
- {% render_table table 'inc/table.html' %} + {% include 'inc/table_controls_htmx.html' with table_modal="DeviceConsolePortTable_config" %} + +
+
+ {% include 'htmx/table.html' %} +
+
{% if perms.dcim.change_consoleport %} @@ -38,6 +42,5 @@ {% endif %}
- {% include 'inc/paginator.html' with paginator=table.paginator page=table.page %} {% table_config_form table %} {% endblock %} diff --git a/netbox/templates/dcim/device/consoleserverports.html b/netbox/templates/dcim/device/consoleserverports.html index 97c98bb9f..f77ef0169 100644 --- a/netbox/templates/dcim/device/consoleserverports.html +++ b/netbox/templates/dcim/device/consoleserverports.html @@ -6,10 +6,14 @@ {% block content %}
{% csrf_token %} - {% include 'inc/table_controls.html' with table_modal="DeviceConsoleServerPortTable_config" %} -
- {% render_table table 'inc/table.html' %} + {% include 'inc/table_controls_htmx.html' with table_modal="DeviceConsoleServerPortTable_config" %} + +
+
+ {% include 'htmx/table.html' %} +
+
{% if perms.dcim.change_consoleserverport %} diff --git a/netbox/templates/dcim/device/devicebays.html b/netbox/templates/dcim/device/devicebays.html index 572e8d110..960610fa8 100644 --- a/netbox/templates/dcim/device/devicebays.html +++ b/netbox/templates/dcim/device/devicebays.html @@ -6,10 +6,14 @@ {% block content %} {% csrf_token %} - {% include 'inc/table_controls.html' with table_modal="DeviceDeviceBayTable_config" %} -
- {% render_table table 'inc/table.html' %} + {% include 'inc/table_controls_htmx.html' with table_modal="DeviceDeviceBayTable_config" %} + +
+
+ {% include 'htmx/table.html' %} +
+
{% if perms.dcim.change_devicebay %} diff --git a/netbox/templates/dcim/device/frontports.html b/netbox/templates/dcim/device/frontports.html index 83dd2dc65..18c08abe4 100644 --- a/netbox/templates/dcim/device/frontports.html +++ b/netbox/templates/dcim/device/frontports.html @@ -6,10 +6,14 @@ {% block content %} {% csrf_token %} - {% include 'inc/table_controls.html' with table_modal="DeviceFrontPortTable_config" %} -
- {% render_table table 'inc/table.html' %} + {% include 'inc/table_controls_htmx.html' with table_modal="DeviceFrontPortTable_config" %} + +
+
+ {% include 'htmx/table.html' %} +
+
{% if perms.dcim.change_frontport %} diff --git a/netbox/templates/dcim/device/interfaces.html b/netbox/templates/dcim/device/interfaces.html index 7d9926fce..c7d86ae89 100644 --- a/netbox/templates/dcim/device/interfaces.html +++ b/netbox/templates/dcim/device/interfaces.html @@ -9,7 +9,15 @@
- +
@@ -34,9 +42,13 @@
-
- {% render_table table 'inc/table.html' %} + +
+
+ {% include 'htmx/table.html' %} +
+
{% if perms.dcim.change_interface %} diff --git a/netbox/templates/dcim/device/inventory.html b/netbox/templates/dcim/device/inventory.html index ee1ce9f52..699ae378f 100644 --- a/netbox/templates/dcim/device/inventory.html +++ b/netbox/templates/dcim/device/inventory.html @@ -6,10 +6,14 @@ {% block content %} {% csrf_token %} - {% include 'inc/table_controls.html' with table_modal="DeviceInventoryItemTable_config" %} -
- {% render_table table 'inc/table.html' %} + {% include 'inc/table_controls_htmx.html' with table_modal="DeviceInventoryItemTable_config" %} + +
+
+ {% include 'htmx/table.html' %} +
+
{% if perms.dcim.change_inventoryitem %} diff --git a/netbox/templates/dcim/device/poweroutlets.html b/netbox/templates/dcim/device/poweroutlets.html index ed49d24b8..af94e1264 100644 --- a/netbox/templates/dcim/device/poweroutlets.html +++ b/netbox/templates/dcim/device/poweroutlets.html @@ -6,10 +6,14 @@ {% block content %} {% csrf_token %} - {% include 'inc/table_controls.html' with table_modal="DevicePowerOutletTable_config" %} -
- {% render_table table 'inc/table.html' %} + {% include 'inc/table_controls_htmx.html' with table_modal="DevicePowerOutletTable_config" %} + +
+
+ {% include 'htmx/table.html' %} +
+
{% if perms.dcim.change_powerport %} diff --git a/netbox/templates/dcim/device/powerports.html b/netbox/templates/dcim/device/powerports.html index 453c5f5b0..7b993bc73 100644 --- a/netbox/templates/dcim/device/powerports.html +++ b/netbox/templates/dcim/device/powerports.html @@ -6,10 +6,14 @@ {% block content %} {% csrf_token %} - {% include 'inc/table_controls.html' with table_modal="DevicePowerPortTable_config" %} -
- {% render_table table 'inc/table.html' %} + {% include 'inc/table_controls_htmx.html' with table_modal="DevicePowerPortTable_config" %} + +
+
+ {% include 'htmx/table.html' %} +
+
{% if perms.dcim.change_powerport %} diff --git a/netbox/templates/dcim/device/rearports.html b/netbox/templates/dcim/device/rearports.html index 37807cd14..86f89e459 100644 --- a/netbox/templates/dcim/device/rearports.html +++ b/netbox/templates/dcim/device/rearports.html @@ -6,10 +6,14 @@ {% block content %} {% csrf_token %} - {% include 'inc/table_controls.html' with table_modal="DeviceRearPortTable_config" %} -
- {% render_table table 'inc/table.html' %} + {% include 'inc/table_controls_htmx.html' with table_modal="DeviceRearPortTable_config" %} + +
+
+ {% include 'htmx/table.html' %} +
+
{% if perms.dcim.change_rearport %} diff --git a/netbox/templates/dcim/devicetype/component_templates.html b/netbox/templates/dcim/devicetype/component_templates.html index d83a232cd..b1e0daf78 100644 --- a/netbox/templates/dcim/devicetype/component_templates.html +++ b/netbox/templates/dcim/devicetype/component_templates.html @@ -7,11 +7,9 @@ {% csrf_token %}
-
- {{ title }} -
-
- {% render_table table 'inc/table.html' %} +
{{ title }}
+
+ {% include 'htmx/table.html' %}