()
| 182 | useEffect(() => { |
| 183 | let cancelled = false; |
| 184 | const loadGuideData = async () => { |
| 185 | try { |
| 186 | setIsChannelsLoading(true); |
| 187 | const params = new URLSearchParams(); |
| 188 | // Group filter by name, if not 'all' |
| 189 | if (selectedGroupId !== 'all') { |
| 190 | const group = channelGroups[Number(selectedGroupId)]; |
| 191 | if (group?.name) params.set('channel_group', group.name); |
| 192 | } else if (!allowAllGroups) { |
| 193 | // If 'all' is not allowed, fall back to first available group |
| 194 | const firstGroup = Object.values(channelGroups).find( |
| 195 | (g) => g?.hasChannels |
| 196 | ); |
| 197 | if (firstGroup?.name) params.set('channel_group', firstGroup.name); |
| 198 | } |
| 199 | |
| 200 | // Profile filter |
| 201 | if (selectedProfileId && selectedProfileId !== 'all') { |
| 202 | params.set('channel_profile_id', String(selectedProfileId)); |
| 203 | } |
| 204 | |
| 205 | // Search filter |
| 206 | if (searchQuery && searchQuery.trim() !== '') { |
| 207 | params.set('search', searchQuery.trim()); |
| 208 | } |
| 209 | |
| 210 | // Fetch channels and programs in parallel — programs don't depend |
| 211 | // on channels so there's no reason to wait for one before the other. |
| 212 | const [channels, programData] = await Promise.all([ |
| 213 | API.getChannelsSummary(params), |
| 214 | fetchPrograms(), |
| 215 | ]); |
| 216 | |
| 217 | if (cancelled) return; |
| 218 | |
| 219 | setGuideChannels(sortChannels(channels || [])); |
| 220 | setPrograms(programData); |
| 221 | } catch (e) { |
| 222 | if (cancelled) return; |
| 223 | console.error('Failed to load guide data:', e); |
| 224 | } finally { |
| 225 | if (!cancelled) { |
| 226 | setIsChannelsLoading(false); |
| 227 | setIsProgramsLoading(false); |
| 228 | } |
| 229 | } |
| 230 | }; |
| 231 | |
| 232 | loadGuideData(); |
| 233 | return () => { |
no test coverage detected