From b24d51c2aa59cb0fb6e6f934e75b88942180cc2d Mon Sep 17 00:00:00 2001 From: dekzter Date: Tue, 17 Feb 2026 17:28:59 -0500 Subject: [PATCH] missed lots of places we used the old channels map, fixed them up --- .../src/components/cards/RecordingCard.jsx | 2 +- frontend/src/components/forms/Recording.jsx | 111 +++++++++++++++++- .../forms/RecordingDetailsModal.jsx | 59 ++++++++-- .../src/components/tables/ChannelsTable.jsx | 34 ++---- frontend/src/pages/Guide.jsx | 7 +- 5 files changed, 172 insertions(+), 41 deletions(-) diff --git a/frontend/src/components/cards/RecordingCard.jsx b/frontend/src/components/cards/RecordingCard.jsx index f5b5b108..e1dbf020 100644 --- a/frontend/src/components/cards/RecordingCard.jsx +++ b/frontend/src/components/cards/RecordingCard.jsx @@ -50,7 +50,7 @@ const RecordingCard = ({ const { timeFormat: timeformat, dateFormat: dateformat } = useDateTimeFormat(); - const channel = channelProp ?? channels?.[recording.channel]; + const channel = channelProp; const customProps = recording.custom_properties || {}; const program = customProps.program || {}; diff --git a/frontend/src/components/forms/Recording.jsx b/frontend/src/components/forms/Recording.jsx index 640dcd18..0bcfc7ee 100644 --- a/frontend/src/components/forms/Recording.jsx +++ b/frontend/src/components/forms/Recording.jsx @@ -11,6 +11,7 @@ import { MultiSelect, Group, TextInput, + Loader, } from '@mantine/core'; import { DateTimePicker, TimeInput, DatePickerInput } from '@mantine/dates'; import { CircleAlert } from 'lucide-react'; @@ -87,10 +88,15 @@ const RecordingModal = ({ isOpen, onClose, }) => { - const channels = useChannelsStore((s) => s.channels); + const channelGroups = useChannelsStore((s) => s.channelGroups); const fetchRecordings = useChannelsStore((s) => s.fetchRecordings); const fetchRecurringRules = useChannelsStore((s) => s.fetchRecurringRules); + // Local state: selected group and channels for that group + const [selectedGroupId, setSelectedGroupId] = useState(null); + const [localChannels, setLocalChannels] = useState([]); + const [isChannelsLoading, setIsChannelsLoading] = useState(false); + const [mode, setMode] = useState('single'); const [submitting, setSubmitting] = useState(false); @@ -210,8 +216,79 @@ const RecordingModal = ({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [isOpen, recording, channel]); + // Initialize group selection to the first available group when modal opens + useEffect(() => { + if (!isOpen) return; + if (selectedGroupId) return; + const firstGroup = Object.values(channelGroups || {}).find( + (g) => g?.hasChannels + ); + if (firstGroup) { + setSelectedGroupId(String(firstGroup.id)); + } + }, [isOpen, selectedGroupId, channelGroups]); + + // Fetch channels for selected group (no global load) + useEffect(() => { + let cancelled = false; + const run = async () => { + if (!isOpen) return; + if (!selectedGroupId) return; + const group = channelGroups?.[Number(selectedGroupId)]; + if (!group?.name) return; + try { + setIsChannelsLoading(true); + const params = new URLSearchParams(); + params.set('channel_group', group.name); + const chans = await API.getChannelsForParams(params); + if (cancelled) return; + setLocalChannels(Array.isArray(chans) ? chans : []); + } catch (e) { + console.warn('Failed to load channels for group', group?.name, e); + if (!cancelled) setLocalChannels([]); + } finally { + if (!cancelled) setIsChannelsLoading(false); + } + }; + run(); + return () => { + cancelled = true; + }; + }, [isOpen, selectedGroupId, channelGroups]); + + // When group changes, clear selected channel in both forms + useEffect(() => { + if (!isOpen) return; + singleForm.setFieldValue('channel_id', ''); + recurringForm.setFieldValue('channel_id', ''); + }, [isOpen, selectedGroupId]); + + // After channels load for a group, auto-select the first channel + useEffect(() => { + if (!isOpen) { + return; + } + if (!selectedGroupId) { + return; + } + const first = Array.isArray(localChannels) ? localChannels[0] : null; + if (!first) { + return; + } + if (mode === 'single') { + if (!singleForm.values.channel_id) { + singleForm.setFieldValue('channel_id', `${first.id}`); + } + } else { + if (!recurringForm.values.channel_id) { + recurringForm.setFieldValue('channel_id', `${first.id}`); + } + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isOpen, selectedGroupId, localChannels, mode]); + const channelOptions = useMemo(() => { - const list = Object.values(channels || {}); + const list = Array.isArray(localChannels) ? [...localChannels] : []; list.sort((a, b) => { const aNum = Number(a.channel_number) || 0; const bNum = Number(b.channel_number) || 0; @@ -222,7 +299,16 @@ const RecordingModal = ({ value: `${item.id}`, label: item.name || `Channel ${item.id}`, })); - }, [channels]); + }, [localChannels]); + + // Group options (no "All") + const groupOptions = useMemo(() => { + const arr = Object.values(channelGroups || {}).filter( + (g) => g?.hasChannels + ); + arr.sort((a, b) => (a?.name || '').localeCompare(b?.name || '')); + return arr.map((g) => ({ value: String(g.id), label: g.name })); + }, [channelGroups]); const resetForms = () => { singleForm.reset(); @@ -333,23 +419,38 @@ const RecordingModal = ({
+ {/* Group selection (required, no "All") */} + : null + } /> ) : (