diff --git a/CHANGELOG.md b/CHANGELOG.md index 71b8b8c1..e01acf2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/frontend/src/components/forms/LiveGroupFilter.jsx b/frontend/src/components/forms/LiveGroupFilter.jsx index b06024e4..09d073f5 100644 --- a/frontend/src/components/forms/LiveGroupFilter.jsx +++ b/frontend/src/components/forms/LiveGroupFilter.jsx @@ -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." /> - + + @@ -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) => ( { 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." /> - + + @@ -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) => (