From f8ec970561081c418aa1448cea586908ecf94be4 Mon Sep 17 00:00:00 2001 From: None Date: Tue, 27 Jan 2026 22:16:33 -0600 Subject: [PATCH] Add TVG-ID column with filter and sort to Streams table Changes: - frontend/src/components/tables/StreamsTable.jsx Added TVG-ID column to streams table Added search filter input in column header Added sort button for ascending/descending order Column follows same pattern as Name, Group, and M3U columns - apps/channels/api_views.py Added TVG-ID to filterable fields Added TVG-ID to sortable fields Enables backend support for search and sorting Users can now view, search, and sort streams by their TVG-ID directly in the Streams table. Fulfills #866 --- apps/channels/api_views.py | 4 +- .../src/components/tables/StreamsTable.jsx | 48 +++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/apps/channels/api_views.py b/apps/channels/api_views.py index 27e07dac..fd79807e 100644 --- a/apps/channels/api_views.py +++ b/apps/channels/api_views.py @@ -106,6 +106,7 @@ class StreamFilter(django_filters.FilterSet): m3u_account_is_active = django_filters.BooleanFilter( field_name="m3u_account__is_active" ) + tvg_id = django_filters.CharFilter(lookup_expr="icontains") class Meta: model = Stream @@ -115,6 +116,7 @@ class StreamFilter(django_filters.FilterSet): "m3u_account", "m3u_account_name", "m3u_account_is_active", + "tvg_id", ] @@ -129,7 +131,7 @@ class StreamViewSet(viewsets.ModelViewSet): filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter] filterset_class = StreamFilter search_fields = ["name", "channel_group__name"] - ordering_fields = ["name", "channel_group__name", "m3u_account__name"] + ordering_fields = ["name", "channel_group__name", "m3u_account__name", "tvg_id"] ordering = ["-name"] def get_permissions(self): diff --git a/frontend/src/components/tables/StreamsTable.jsx b/frontend/src/components/tables/StreamsTable.jsx index 64c087ef..3ac06ac1 100644 --- a/frontend/src/components/tables/StreamsTable.jsx +++ b/frontend/src/components/tables/StreamsTable.jsx @@ -370,6 +370,25 @@ const StreamsTable = ({ onReady }) => { ), }, + { + header: 'TVG-ID', + id: 'tvg_id', + accessorKey: 'tvg_id', + size: columnSizing.tvg_id || 120, + cell: ({ getValue }) => ( + + + {getValue()} + + + ), + }, ], [channelGroups, playlists, columnSizing] ); @@ -427,6 +446,7 @@ const StreamsTable = ({ onReady }) => { name: 'name', group: 'channel_group__name', m3u: 'm3u_account__name', + tvg_id: 'tvg_id', }; const sortField = fieldMapping[columnId] || columnId; const sortDirection = sorting[0].desc ? '-' : ''; @@ -967,6 +987,33 @@ const StreamsTable = ({ onReady }) => { ); } + + case 'tvg_id': + return ( + + e.stopPropagation()} + onChange={handleFilterChange} + size="xs" + variant="unstyled" + className="table-input-header" + leftSection={} + style={{ flex: 1, minWidth: 0 }} + rightSectionPointerEvents="auto" + rightSection={React.createElement(sortingIcon, { + onClick: (e) => { + e.stopPropagation(); + onSortingChange('tvg_id'); + }, + size: 14, + style: { cursor: 'pointer' }, + })} + /> + + ); } }; @@ -1019,6 +1066,7 @@ const StreamsTable = ({ onReady }) => { name: renderHeaderCell, group: renderHeaderCell, m3u: renderHeaderCell, + tvg_id: renderHeaderCell, }, bodyCellRenderFns: { actions: renderBodyCell,