mirror of
https://github.com/Dispatcharr/Dispatcharr.git
synced 2026-07-20 01:59:50 +00:00
Enhancement: Status filter for M3U group and VOD category filter modals: A new **All / Enabled / Disabled** segmented control is now shown alongside the text search input in the Live, VOD - Movies, and VOD - Series tabs of the M3U Group Filter modal. The status filter works in combination with the text search and also scopes the "Select Visible" / "Deselect Visible" buttons so they only act on the currently visible subset. (Closes #312)
This commit is contained in:
parent
161150f0f8
commit
ba0036445c
3 changed files with 58 additions and 20 deletions
|
|
@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- Status filter for M3U group and VOD category filter modals: A new **All / Enabled / Disabled** segmented control is now shown alongside the text search input in the Live, VOD - Movies, and VOD - Series tabs of the M3U Group Filter modal. The status filter works in combination with the text search and also scopes the "Select Visible" / "Deselect Visible" buttons so they only act on the currently visible subset. (Closes #312)
|
||||
|
||||
## [0.21.1] - 2026-03-18
|
||||
|
||||
### Fixed
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import {
|
|||
Popover,
|
||||
ScrollArea,
|
||||
Center,
|
||||
SegmentedControl,
|
||||
} from '@mantine/core';
|
||||
import { Info } from 'lucide-react';
|
||||
import useChannelsStore from '../../store/channels';
|
||||
|
|
@ -54,6 +55,7 @@ const LiveGroupFilter = ({
|
|||
const streamProfiles = useStreamProfilesStore((s) => s.profiles);
|
||||
const fetchStreamProfiles = useStreamProfilesStore((s) => s.fetchProfiles);
|
||||
const [groupFilter, setGroupFilter] = useState('');
|
||||
const [statusFilter, setStatusFilter] = useState('all');
|
||||
const [epgSources, setEpgSources] = useState([]);
|
||||
|
||||
// Logo selection functionality
|
||||
|
|
@ -177,13 +179,22 @@ const LiveGroupFilter = ({
|
|||
setCurrentEditingGroupId(null);
|
||||
};
|
||||
|
||||
const isVisible = (group) => {
|
||||
const matchesText = group.name
|
||||
.toLowerCase()
|
||||
.includes(groupFilter.toLowerCase());
|
||||
const matchesStatus =
|
||||
statusFilter === 'all' ||
|
||||
(statusFilter === 'enabled' && group.enabled) ||
|
||||
(statusFilter === 'disabled' && !group.enabled);
|
||||
return matchesText && matchesStatus;
|
||||
};
|
||||
|
||||
const selectAll = () => {
|
||||
setGroupStates(
|
||||
groupStates.map((state) => ({
|
||||
...state,
|
||||
enabled: state.name.toLowerCase().includes(groupFilter.toLowerCase())
|
||||
? true
|
||||
: state.enabled,
|
||||
enabled: isVisible(state) ? true : state.enabled,
|
||||
}))
|
||||
);
|
||||
};
|
||||
|
|
@ -192,9 +203,7 @@ const LiveGroupFilter = ({
|
|||
setGroupStates(
|
||||
groupStates.map((state) => ({
|
||||
...state,
|
||||
enabled: state.name.toLowerCase().includes(groupFilter.toLowerCase())
|
||||
? false
|
||||
: state.enabled,
|
||||
enabled: isVisible(state) ? false : state.enabled,
|
||||
}))
|
||||
);
|
||||
};
|
||||
|
|
@ -220,7 +229,7 @@ const LiveGroupFilter = ({
|
|||
description="When disabled, new groups from the M3U source will be created but disabled by default. You can enable them manually later."
|
||||
/>
|
||||
|
||||
<Flex gap="sm">
|
||||
<Flex gap="sm" align="center">
|
||||
<TextInput
|
||||
placeholder="Filter groups..."
|
||||
value={groupFilter}
|
||||
|
|
@ -228,6 +237,16 @@ const LiveGroupFilter = ({
|
|||
style={{ flex: 1 }}
|
||||
size="xs"
|
||||
/>
|
||||
<SegmentedControl
|
||||
value={statusFilter}
|
||||
onChange={setStatusFilter}
|
||||
size="xs"
|
||||
data={[
|
||||
{ label: 'All', value: 'all' },
|
||||
{ label: 'Enabled', value: 'enabled' },
|
||||
{ label: 'Disabled', value: 'disabled' },
|
||||
]}
|
||||
/>
|
||||
<Button variant="default" size="xs" onClick={selectAll}>
|
||||
Select Visible
|
||||
</Button>
|
||||
|
|
@ -245,9 +264,7 @@ const LiveGroupFilter = ({
|
|||
verticalSpacing="xs"
|
||||
>
|
||||
{groupStates
|
||||
.filter((group) =>
|
||||
group.name.toLowerCase().includes(groupFilter.toLowerCase())
|
||||
)
|
||||
.filter((group) => isVisible(group))
|
||||
.sort((a, b) => a.name.localeCompare(b.name))
|
||||
.map((group) => (
|
||||
<Group
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import {
|
|||
Divider,
|
||||
Box,
|
||||
Checkbox,
|
||||
SegmentedControl,
|
||||
} from '@mantine/core';
|
||||
import { CircleCheck, CircleX } from 'lucide-react';
|
||||
import useVODStore from '../../store/useVODStore';
|
||||
|
|
@ -25,6 +26,7 @@ const VODCategoryFilter = ({
|
|||
}) => {
|
||||
const categories = useVODStore((s) => s.categories);
|
||||
const [filter, setFilter] = useState('');
|
||||
const [statusFilter, setStatusFilter] = useState('all');
|
||||
|
||||
useEffect(() => {
|
||||
if (Object.keys(categories).length === 0) {
|
||||
|
|
@ -64,13 +66,22 @@ const VODCategoryFilter = ({
|
|||
);
|
||||
};
|
||||
|
||||
const isVisible = (category) => {
|
||||
const matchesText = category.name
|
||||
.toLowerCase()
|
||||
.includes(filter.toLowerCase());
|
||||
const matchesStatus =
|
||||
statusFilter === 'all' ||
|
||||
(statusFilter === 'enabled' && category.enabled) ||
|
||||
(statusFilter === 'disabled' && !category.enabled);
|
||||
return matchesText && matchesStatus;
|
||||
};
|
||||
|
||||
const selectAll = () => {
|
||||
setCategoryStates(
|
||||
categoryStates.map((state) => ({
|
||||
...state,
|
||||
enabled: state.name.toLowerCase().includes(filter.toLowerCase())
|
||||
? true
|
||||
: state.enabled,
|
||||
enabled: isVisible(state) ? true : state.enabled,
|
||||
}))
|
||||
);
|
||||
};
|
||||
|
|
@ -79,9 +90,7 @@ const VODCategoryFilter = ({
|
|||
setCategoryStates(
|
||||
categoryStates.map((state) => ({
|
||||
...state,
|
||||
enabled: state.name.toLowerCase().includes(filter.toLowerCase())
|
||||
? false
|
||||
: state.enabled,
|
||||
enabled: isVisible(state) ? false : state.enabled,
|
||||
}))
|
||||
);
|
||||
};
|
||||
|
|
@ -98,7 +107,7 @@ const VODCategoryFilter = ({
|
|||
description="When disabled, new categories from the provider will be created but disabled by default. You can enable them manually later."
|
||||
/>
|
||||
|
||||
<Flex gap="sm">
|
||||
<Flex gap="sm" align="center">
|
||||
<TextInput
|
||||
placeholder="Filter categories..."
|
||||
value={filter}
|
||||
|
|
@ -106,6 +115,16 @@ const VODCategoryFilter = ({
|
|||
style={{ flex: 1 }}
|
||||
size="xs"
|
||||
/>
|
||||
<SegmentedControl
|
||||
value={statusFilter}
|
||||
onChange={setStatusFilter}
|
||||
size="xs"
|
||||
data={[
|
||||
{ label: 'All', value: 'all' },
|
||||
{ label: 'Enabled', value: 'enabled' },
|
||||
{ label: 'Disabled', value: 'disabled' },
|
||||
]}
|
||||
/>
|
||||
<Button variant="default" size="xs" onClick={selectAll}>
|
||||
Select Visible
|
||||
</Button>
|
||||
|
|
@ -121,9 +140,7 @@ const VODCategoryFilter = ({
|
|||
verticalSpacing="xs"
|
||||
>
|
||||
{categoryStates
|
||||
.filter((category) => {
|
||||
return category.name.toLowerCase().includes(filter.toLowerCase());
|
||||
})
|
||||
.filter((category) => isVisible(category))
|
||||
.sort((a, b) => a.name.localeCompare(b.name))
|
||||
.map((category) => (
|
||||
<Group
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue