- Fixed authentication race condition where `isAuthenticated` was set before data loading completed, causing routes to render and tables to mount prematurely
- Added `isInitialized` flag to delay route rendering until after all initialization data is loaded via `initData()`
- Consolidated version and environment settings fetching into centralized settings store with caching to prevent redundant calls
- Implemented stale fetch prevention in ChannelsTable and StreamsTable using fetch version tracking to ignore outdated responses
- Fixed filter handling in tables to use `debouncedFilters` consistently, preventing unnecessary refetches
- Added initialization guards using refs to prevent double-execution of auth and superuser checks during React StrictMode's intentional double-rendering in development
- Removed duplicate version/environment fetch calls from Sidebar, LoginForm, and SuperuserForm by using centralized store
- Added a robust inline editing mode for the channels table, allowing users to quickly edit channel fields (name, number, group, EPG, logo) directly in the table without opening a modal.
- EPG and logo columns now support searchable dropdowns with instant filtering and keyboard navigation for fast assignment.
- Drag-and-drop reordering of channels enabled when unlocked, with persistent order updates.
- Group column now uses a searchable dropdown for quick group assignment, matching the UX of EPG and logo selectors.
- All editable cells provide clear focus, hover, and disabled states for improved accessibility and usability.
- Table unlock/edit mode toggle with clear visual cues and safe-guarded save logic to prevent accidental edits.
- All changes are saved via API with optimistic UI updates and error handling.
Streams table button labels: Renamed "Remove" to "Delete" and "Add Stream to Channel" to "Add to Channel" for clarity and consistency with other UI terminology.
- Currently playing program title displayed with live broadcast indicator (green Radio icon)
- Expandable program descriptions via chevron button
- Efficient POST-based API endpoint (`/api/epg/current-programs/`) supporting batch channel queries or fetching all channels
- Smart scheduling that fetches new program data 5 seconds after current program ends
- Only polls when active channel list changes, not on stats refresh
- Channel preview button: Added preview functionality to active stream cards on stats page
- Added `is_adult` boolean field to both Stream and Channel models with database indexing for efficient filtering and sorting
- Automatically populated during M3U/XC refresh operations by extracting `is_adult` value from provider data
- Type-safe conversion supporting both integer (0/1) and string ("0"/"1") formats from different providers
- UI controls in channel edit form (Switch with tooltip) and bulk edit form (Select dropdown) for easy management
- XtreamCodes API support with proper integer formatting (0/1) in live stream responses
- Automatic propagation from streams to channels during both single and bulk channel creation operations
- Included in serializers for full API support
- User-level content filtering: Non-admin users can opt to hide mature content channels across all interfaces (web UI, M3U playlists, EPG data, XtreamCodes API) via "Hide Mature Content" toggle in user settings (stored in custom_properties, admin users always see all content)
- Added functionality to pin/unpin table headers, maintaining visibility while scrolling. This feature is accessible via the channel table menu and UI Settings page, with persistence across sessions.
- Refactored table preferences management by migrating `table-size` preference to a centralized `useTablePreferences` hook, enhancing maintainability and consistency across table components.
When a stream was added to a channel (via "Add to Channel" button, "Create New Channel", or bulk "Add Streams to Channel"), the StreamsTable data wasn't being refetched. This caused streams to remain visible even when the "Only Unassociated" filter was active, despite no longer being unassociated.
Fixed by calling fetchData() after channel updates in:
- StreamRowActions.addStreamToChannel (single stream to channel)
- addStreamsToChannel (bulk add to channel)
- executeSingleChannelCreation (create new channel from stream)
Now streams immediately disappear from the filtered view when associated with a channel.
The Group column header was overflowing vertically when multiple groups were selected in the MultiSelect filter. Initial attempts to preserve sorting functionality while fixing the overflow were unsuccessful due to how Mantine's MultiSelect rightSection positioning works.
Final solution removes the sorting icon from the Group column to match ChannelsTable's implementation, where channel_group also uses a MultiSelect filter without sorting. This allows the header to properly expand vertically to accommodate selected filter values without overflow.
Resolves#613
Adds a filter button with 'Only Unassociated' toggle option to the Streams table, allowing users to quickly identify streams from providers that are not associated with any channel. The filter button follows the existing UI pattern from ChannelTableHeader, positioned on the right side of the toolbar. Leverages existing backend support for the unassigned query parameter.
Resolves#667