mirror of
https://github.com/Dispatcharr/Dispatcharr.git
synced 2026-01-23 02:35:14 +00:00
advanced filtering for hiding disabled channels and viewing only empty channels
This commit is contained in:
parent
0bfd06a5a3
commit
ea38c0b4b8
3 changed files with 78 additions and 23 deletions
|
|
@ -8,6 +8,7 @@ from drf_yasg.utils import swagger_auto_schema
|
||||||
from drf_yasg import openapi
|
from drf_yasg import openapi
|
||||||
from django.shortcuts import get_object_or_404, get_list_or_404
|
from django.shortcuts import get_object_or_404, get_list_or_404
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
|
from django.db.models import Q
|
||||||
import os, json, requests, logging
|
import os, json, requests, logging
|
||||||
from apps.accounts.permissions import (
|
from apps.accounts.permissions import (
|
||||||
Authenticated,
|
Authenticated,
|
||||||
|
|
@ -419,31 +420,36 @@ class ChannelViewSet(viewsets.ModelViewSet):
|
||||||
group_names = channel_group.split(",")
|
group_names = channel_group.split(",")
|
||||||
qs = qs.filter(channel_group__name__in=group_names)
|
qs = qs.filter(channel_group__name__in=group_names)
|
||||||
|
|
||||||
|
filters = {}
|
||||||
|
q_filters = Q()
|
||||||
|
|
||||||
channel_profile_id = self.request.query_params.get("channel_profile_id")
|
channel_profile_id = self.request.query_params.get("channel_profile_id")
|
||||||
show_disabled_param = self.request.query_params.get("show_disabled", None)
|
show_disabled_param = self.request.query_params.get("show_disabled", None)
|
||||||
|
only_streamless = self.request.query_params.get("only_streamless", None)
|
||||||
|
|
||||||
if channel_profile_id:
|
if channel_profile_id:
|
||||||
try:
|
try:
|
||||||
profile_id_int = int(channel_profile_id)
|
profile_id_int = int(channel_profile_id)
|
||||||
# If show_disabled is present, include all memberships for that profile.
|
filters["channelprofilemembership__channel_profile_id"] = profile_id_int
|
||||||
# If absent, restrict to enabled=True.
|
|
||||||
if show_disabled_param is None:
|
if show_disabled_param is None:
|
||||||
qs = qs.filter(
|
filters["channelprofilemembership__enabled"] = True
|
||||||
channelprofilemembership__channel_profile_id=profile_id_int,
|
|
||||||
channelprofilemembership__enabled=True,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
qs = qs.filter(
|
|
||||||
channelprofilemembership__channel_profile_id=profile_id_int
|
|
||||||
)
|
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
# Ignore invalid profile id values
|
# Ignore invalid profile id values
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if self.request.user.user_level < 10:
|
if only_streamless:
|
||||||
qs = qs.filter(user_level__lte=self.request.user.user_level)
|
q_filters &= Q(streams__isnull=True)
|
||||||
|
|
||||||
return qs
|
if self.request.user.user_level < 10:
|
||||||
|
filters["user_level__lte"] = self.request.user.user_level
|
||||||
|
|
||||||
|
if filters:
|
||||||
|
qs = qs.filter(**filters)
|
||||||
|
if q_filters:
|
||||||
|
qs = qs.filter(q_filters)
|
||||||
|
|
||||||
|
return qs.distinct()
|
||||||
|
|
||||||
def get_serializer_context(self):
|
def get_serializer_context(self):
|
||||||
context = super().get_serializer_context()
|
context = super().get_serializer_context()
|
||||||
|
|
|
||||||
|
|
@ -294,6 +294,8 @@ const ChannelsTable = ({}) => {
|
||||||
profiles[selectedProfileId]
|
profiles[selectedProfileId]
|
||||||
);
|
);
|
||||||
const [showDisabled, setShowDisabled] = useState(true);
|
const [showDisabled, setShowDisabled] = useState(true);
|
||||||
|
const [showOnlyStreamlessChannels, setShowOnlyStreamlessChannels] =
|
||||||
|
useState(false);
|
||||||
|
|
||||||
const [paginationString, setPaginationString] = useState('');
|
const [paginationString, setPaginationString] = useState('');
|
||||||
const [filters, setFilters] = useState({
|
const [filters, setFilters] = useState({
|
||||||
|
|
@ -380,6 +382,9 @@ const ChannelsTable = ({}) => {
|
||||||
if (showDisabled === true) {
|
if (showDisabled === true) {
|
||||||
params.append('show_disabled', true);
|
params.append('show_disabled', true);
|
||||||
}
|
}
|
||||||
|
if (showOnlyStreamlessChannels === true) {
|
||||||
|
params.append('only_streamless', true);
|
||||||
|
}
|
||||||
|
|
||||||
// Apply sorting
|
// Apply sorting
|
||||||
if (sorting.length > 0) {
|
if (sorting.length > 0) {
|
||||||
|
|
@ -412,7 +417,14 @@ const ChannelsTable = ({}) => {
|
||||||
pageSize: pagination.pageSize,
|
pageSize: pagination.pageSize,
|
||||||
});
|
});
|
||||||
setAllRowIds(ids);
|
setAllRowIds(ids);
|
||||||
}, [pagination, sorting, debouncedFilters, showDisabled, selectedProfileId]);
|
}, [
|
||||||
|
pagination,
|
||||||
|
sorting,
|
||||||
|
debouncedFilters,
|
||||||
|
showDisabled,
|
||||||
|
selectedProfileId,
|
||||||
|
showOnlyStreamlessChannels,
|
||||||
|
]);
|
||||||
|
|
||||||
const stopPropagation = useCallback((e) => {
|
const stopPropagation = useCallback((e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
@ -1340,6 +1352,8 @@ const ChannelsTable = ({}) => {
|
||||||
table={table}
|
table={table}
|
||||||
showDisabled={showDisabled}
|
showDisabled={showDisabled}
|
||||||
setShowDisabled={setShowDisabled}
|
setShowDisabled={setShowDisabled}
|
||||||
|
showOnlyStreamlessChannels={showOnlyStreamlessChannels}
|
||||||
|
setShowOnlyStreamlessChannels={setShowOnlyStreamlessChannels}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Table or ghost empty state inside Paper */}
|
{/* Table or ghost empty state inside Paper */}
|
||||||
|
|
|
||||||
|
|
@ -12,15 +12,12 @@ import {
|
||||||
Text,
|
Text,
|
||||||
TextInput,
|
TextInput,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
UnstyledButton,
|
|
||||||
useMantineTheme,
|
useMantineTheme,
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import {
|
import {
|
||||||
ArrowDown01,
|
ArrowDown01,
|
||||||
Binary,
|
Binary,
|
||||||
Check,
|
|
||||||
CircleCheck,
|
CircleCheck,
|
||||||
Ellipsis,
|
|
||||||
EllipsisVertical,
|
EllipsisVertical,
|
||||||
SquareMinus,
|
SquareMinus,
|
||||||
SquarePen,
|
SquarePen,
|
||||||
|
|
@ -28,6 +25,9 @@ import {
|
||||||
Settings,
|
Settings,
|
||||||
Eye,
|
Eye,
|
||||||
EyeOff,
|
EyeOff,
|
||||||
|
Filter,
|
||||||
|
Square,
|
||||||
|
SquareCheck,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import API from '../../../api';
|
import API from '../../../api';
|
||||||
import { notifications } from '@mantine/notifications';
|
import { notifications } from '@mantine/notifications';
|
||||||
|
|
@ -106,6 +106,8 @@ const ChannelTableHeader = ({
|
||||||
selectedTableIds,
|
selectedTableIds,
|
||||||
showDisabled,
|
showDisabled,
|
||||||
setShowDisabled,
|
setShowDisabled,
|
||||||
|
showOnlyStreamlessChannels,
|
||||||
|
setShowOnlyStreamlessChannels,
|
||||||
}) => {
|
}) => {
|
||||||
const theme = useMantineTheme();
|
const theme = useMantineTheme();
|
||||||
|
|
||||||
|
|
@ -216,6 +218,10 @@ const ChannelTableHeader = ({
|
||||||
setShowDisabled(!showDisabled);
|
setShowDisabled(!showDisabled);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const toggleShowOnlyStreamlessChannels = () => {
|
||||||
|
setShowOnlyStreamlessChannels(!showOnlyStreamlessChannels);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Group justify="space-between">
|
<Group justify="space-between">
|
||||||
<Group gap={5} style={{ paddingLeft: 10 }}>
|
<Group gap={5} style={{ paddingLeft: 10 }}>
|
||||||
|
|
@ -234,12 +240,6 @@ const ChannelTableHeader = ({
|
||||||
<Tooltip label="Create Profile">
|
<Tooltip label="Create Profile">
|
||||||
<CreateProfilePopover />
|
<CreateProfilePopover />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
<Tooltip label={showDisabled ? 'Hide Disabled' : 'Show Disabled'}>
|
|
||||||
<Button size="xs" variant="default" onClick={toggleShowDisabled}>
|
|
||||||
{showDisabled ? <Eye size={18} /> : <EyeOff size={18} />}
|
|
||||||
</Button>
|
|
||||||
</Tooltip>
|
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
|
|
@ -250,6 +250,41 @@ const ChannelTableHeader = ({
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Flex gap={6}>
|
<Flex gap={6}>
|
||||||
|
<Menu shadow="md" width={200}>
|
||||||
|
<Menu.Target>
|
||||||
|
<Button size="xs" variant="default" onClick={() => {}}>
|
||||||
|
<Filter size={18} />
|
||||||
|
</Button>
|
||||||
|
</Menu.Target>
|
||||||
|
|
||||||
|
<Menu.Dropdown>
|
||||||
|
<Menu.Item
|
||||||
|
onClick={toggleShowDisabled}
|
||||||
|
leftSection={
|
||||||
|
showDisabled ? <Eye size={18} /> : <EyeOff size={18} />
|
||||||
|
}
|
||||||
|
disabled={selectedProfileId === '0'}
|
||||||
|
>
|
||||||
|
<Text size="xs">
|
||||||
|
{showDisabled ? 'Hide Disabled' : 'Show Disabled'}
|
||||||
|
</Text>
|
||||||
|
</Menu.Item>
|
||||||
|
|
||||||
|
<Menu.Item
|
||||||
|
onClick={toggleShowOnlyStreamlessChannels}
|
||||||
|
leftSection={
|
||||||
|
showOnlyStreamlessChannels ? (
|
||||||
|
<SquareCheck size={18} />
|
||||||
|
) : (
|
||||||
|
<Square size={18} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Text size="xs">Only Empty Channels</Text>
|
||||||
|
</Menu.Item>
|
||||||
|
</Menu.Dropdown>
|
||||||
|
</Menu>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
leftSection={<SquarePen size={18} />}
|
leftSection={<SquarePen size={18} />}
|
||||||
variant="default"
|
variant="default"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue