diff --git a/tests/frontend/test_host.py b/tests/frontend/test_host.py index 84192bfe3..40029717e 100644 --- a/tests/frontend/test_host.py +++ b/tests/frontend/test_host.py @@ -185,5 +185,5 @@ def test_user_host_without_room(client, host_without_room): ) assert len(resp.json["items"]) == 1 [it] = resp.json["items"] - assert it["switch"] is None + assert it["switch"] == [] assert it["port"] is None diff --git a/web/blueprints/host/__init__.py b/web/blueprints/host/__init__.py index 2f633afd4..b20f8bc55 100644 --- a/web/blueprints/host/__init__.py +++ b/web/blueprints/host/__init__.py @@ -20,7 +20,7 @@ from web.blueprints.helpers.user import get_user_or_404 from web.blueprints.host.forms import InterfaceForm, HostForm from web.blueprints.host.tables import InterfaceTable, HostTable, HostRow, InterfaceRow -from web.table.table import TableResponse, BtnColResponse +from web.table.table import TableResponse, BtnColResponse, LinkColResponse bp = Blueprint('host', __name__) access = BlueprintAccess(bp, required_properties=['user_show']) @@ -330,17 +330,22 @@ def default_response() -> ResponseReturnValue: def _host_row(host: Host, user_id: int) -> HostRow: if host.room: patch_ports = host.room.connected_patch_ports - switches = ", ".join( - p.switch_port.switch.host.name or "" for p in patch_ports - ) + switches = [p.switch_port.switch.host for p in patch_ports] + switch_links = [ + LinkColResponse( + href=url_for("infrastructure.switch_show", switch_id=s.id), + title=s.name or f"", + ) + for s in switches + ] ports = ", ".join(p.switch_port.name for p in patch_ports) else: - switches = None + switch_links = [] ports = None return HostRow( id=host.id, name=host.name, - switch=switches, + switch=switch_links, port=ports, actions=[ BtnColResponse( diff --git a/web/blueprints/host/tables.py b/web/blueprints/host/tables.py index 44d9713c4..5bad8a126 100644 --- a/web/blueprints/host/tables.py +++ b/web/blueprints/host/tables.py @@ -11,6 +11,8 @@ button_toolbar, MultiBtnColumn, BtnColResponse, + MultiLinkColumn, + LinkColResponse, ) @@ -18,7 +20,7 @@ class HostTable(BootstrapTable): """A table for displaying hosts """ name = Column("Name") - switch = Column("Switch") + switch = MultiLinkColumn("Switch") port = Column("SwitchPort") actions = MultiBtnColumn("Aktionen", hide_if=no_hosts_change, width=3) interfaces_table_link = Column("", hide_if=lambda: True) @@ -49,7 +51,7 @@ def toolbar(self) -> HasDunderStr | None: class HostRow(BaseModel): name: str | None = None - switch: str | None = None + switch: list[LinkColResponse] | None = None port: str | None = None actions: list[BtnColResponse] interfaces_table_link: str diff --git a/web/blueprints/infrastructure/__init__.py b/web/blueprints/infrastructure/__init__.py index c86766ee9..0e13c7250 100644 --- a/web/blueprints/infrastructure/__init__.py +++ b/web/blueprints/infrastructure/__init__.py @@ -146,7 +146,10 @@ def switch_show(switch_id: int) -> ResponseValue: switch=switch, port_table=PortTable( data_url=url_for('.switch_show_json', switch_id=switch.host_id), - switch_id=switch_id)) + switch_id=switch_id, + switch_room_id=switch.host.room_id, + ), + ) @bp.route('/switch/show//json') diff --git a/web/blueprints/infrastructure/tables.py b/web/blueprints/infrastructure/tables.py index 60bdf7c89..9235016df 100644 --- a/web/blueprints/infrastructure/tables.py +++ b/web/blueprints/infrastructure/tables.py @@ -4,7 +4,7 @@ from flask_login import current_user from pydantic import BaseModel, ConfigDict -from web.table.lazy_join import HasDunderStr +from web.table.lazy_join import HasDunderStr, lazy_join from web.table.table import ( BootstrapTable, Column, @@ -85,9 +85,10 @@ class Meta: 'data-sort-name': 'switchport_name', } - def __init__(self, *, switch_id: int | None = None, **kw: t.Any) -> None: + def __init__(self, *, switch_id: int, switch_room_id: int, **kw: t.Any) -> None: super().__init__(**kw) self.switch_id = switch_id + self.switch_room_id = switch_room_id switchport_name = Column("Name", width=2, col_args={'data-sorter': 'table.sortPort'}) patchport_name = Column("→ Patchport", width=2, col_args={'data-sorter': 'table.sortPatchPort'}) @@ -96,11 +97,19 @@ def __init__(self, *, switch_id: int | None = None, **kw: t.Any) -> None: delete_link = BtnColumn('Löschen', hide_if=no_inf_change) @property - def toolbar(self) -> HasDunderStr | None: + @lazy_join + def toolbar(self) -> t.Iterator[HasDunderStr | None]: if no_inf_change(): return None - href = url_for(".switch_port_create", switch_id=self.switch_id) - return button_toolbar("Switch-Port", href) + + yield from button_toolbar( + "Switch-Port", + url_for(".switch_port_create", switch_id=self.switch_id), + ) + yield from button_toolbar( + "Patch-Port", + url_for("facilities.patch_port_create", switch_room_id=self.switch_room_id), + ) class PortRow(BaseModel): diff --git a/web/resources/js/table.js b/web/resources/js/table.js index 1fb4ff94d..e40182dbd 100644 --- a/web/resources/js/table.js +++ b/web/resources/js/table.js @@ -174,6 +174,16 @@ export function multiBtnFormatter(value, row, index) { return btnFormatter(value, row, index); } +export function multiLinkFormatter(value, row, index) { + if (!value) { + return; + } + if (Array.isArray(value)) { + return value.map(v => linkFormatter(v, row, index)).join(' '); + } + return linkFormatter(value, row, index); +} + export function listFormatter(value, row, index) { if (!value) { return; diff --git a/web/table/table.py b/web/table/table.py index 6b2491fcf..67a131be2 100644 --- a/web/table/table.py +++ b/web/table/table.py @@ -191,6 +191,13 @@ def value( ... +@custom_formatter_column("table.multiLinkFormatter") +class MultiLinkColumn(Column): + def __init__(self, *a: t.Any, **kw: t.Any) -> None: + kw.setdefault("sortable", False) + super().__init__(*a, **kw) + + @custom_formatter_column('table.dateFormatter') class DateColumn(Column):