diff --git a/frontend/src/components/forms/OutputProfile.jsx b/frontend/src/components/forms/OutputProfile.jsx index 541c911b..610c0ef4 100644 --- a/frontend/src/components/forms/OutputProfile.jsx +++ b/frontend/src/components/forms/OutputProfile.jsx @@ -1,8 +1,5 @@ import React, { useEffect, useMemo, useState } from 'react'; import { useForm } from 'react-hook-form'; -import { yupResolver } from '@hookform/resolvers/yup'; -import * as Yup from 'yup'; -import API from '../../api'; import { Modal, TextInput, @@ -13,27 +10,14 @@ import { Stack, Checkbox, } from '@mantine/core'; - -const BUILT_IN_COMMANDS = [ - { value: 'ffmpeg', label: 'FFmpeg' }, - { value: '__custom__', label: 'Custom…' }, -]; - -const COMMAND_EXAMPLES = { - ffmpeg: - '-i pipe:0 -c:v libx264 -b:v 2000k -vf scale=-2:720 -c:a copy -f mpegts pipe:1', -}; - -const toCommandSelection = (command) => - BUILT_IN_COMMANDS.find((o) => o.value === command && o.value !== '__custom__') - ? command - : '__custom__'; - -const schema = Yup.object({ - name: Yup.string().required('Name is required'), - command: Yup.string().required('Command is required'), - parameters: Yup.string(), -}); +import { + addOutputProfile, + BUILT_IN_COMMANDS, + COMMAND_EXAMPLES, + getResolver, + toCommandSelection, + updateOutputProfile, +} from '../../utils/forms/OutputProfileUtils'; const OutputProfile = ({ profile = null, isOpen, onClose }) => { const [commandSelection, setCommandSelection] = useState('ffmpeg'); @@ -57,7 +41,7 @@ const OutputProfile = ({ profile = null, isOpen, onClose }) => { watch, } = useForm({ defaultValues, - resolver: yupResolver(schema), + resolver: getResolver(), }); useEffect(() => { @@ -67,9 +51,9 @@ const OutputProfile = ({ profile = null, isOpen, onClose }) => { const onSubmit = async (values) => { if (profile?.id) { - await API.updateOutputProfile({ id: profile.id, ...values }); + await updateOutputProfile({ id: profile.id, ...values }); } else { - await API.addOutputProfile(values); + await addOutputProfile(values); } reset(); onClose(); diff --git a/frontend/src/components/forms/ServerGroup.jsx b/frontend/src/components/forms/ServerGroup.jsx index 80c8c1a4..0d5ecd05 100644 --- a/frontend/src/components/forms/ServerGroup.jsx +++ b/frontend/src/components/forms/ServerGroup.jsx @@ -1,20 +1,13 @@ import React, { useEffect, useMemo } from 'react'; import { useForm } from 'react-hook-form'; -import { yupResolver } from '@hookform/resolvers/yup'; -import * as Yup from 'yup'; -import API from '../../api'; import { Button, Flex, Modal, TextInput } from '@mantine/core'; +import { + getResolver, + updateServerGroup, + addServerGroup, +} from '../../utils/forms/ServerGroupUtils'; -const schema = Yup.object({ - name: Yup.string().required('Name is required'), -}); - -const ServerGroupForm = ({ - serverGroup = null, - isOpen, - onClose, - onSaved, -}) => { +const ServerGroupForm = ({ serverGroup = null, isOpen, onClose, onSaved }) => { const defaultValues = useMemo( () => ({ name: serverGroup?.name || '', @@ -29,16 +22,13 @@ const ServerGroupForm = ({ reset, } = useForm({ defaultValues, - resolver: yupResolver(schema), + resolver: getResolver(), }); const onSubmit = async (values) => { - let response; - if (serverGroup?.id) { - response = await API.updateServerGroup({ id: serverGroup.id, ...values }); - } else { - response = await API.addServerGroup(values); - } + const response = serverGroup?.id + ? await updateServerGroup({ id: serverGroup.id, ...values }) + : await addServerGroup(values); if (response) { onSaved?.(response); diff --git a/frontend/src/components/modals/CreateChannelModal.jsx b/frontend/src/components/modals/CreateChannelModal.jsx index a1a42b29..4f4d9d4e 100644 --- a/frontend/src/components/modals/CreateChannelModal.jsx +++ b/frontend/src/components/modals/CreateChannelModal.jsx @@ -4,6 +4,7 @@ import { Stack, Text, Radio, + RadioGroup, NumberInput, Checkbox, Group, @@ -101,11 +102,7 @@ const CreateChannelModal = ({ - + - + {mode === customModeValue && ( { + return API.updateChannelProfile(values); +} + +const duplicateChannelProfile = (profileId, newName) => { + return API.duplicateChannelProfile(profileId, newName); +} + const ProfileModal = ({ opened, onClose, mode, profile }) => { const [profileNameInput, setProfileNameInput] = useState(''); const setSelectedProfileId = useChannelsStore((s) => s.setSelectedProfileId); @@ -40,7 +48,7 @@ const ProfileModal = ({ opened, onClose, mode, profile }) => { if (!mode || !profile) return; if (!trimmedName) { - notifications.show({ + showNotification({ title: 'Profile name is required', color: 'red.5', }); @@ -53,13 +61,13 @@ const ProfileModal = ({ opened, onClose, mode, profile }) => { return; } - const updatedProfile = await API.updateChannelProfile({ + const updatedProfile = await updateChannelProfile({ id: profile.id, name: trimmedName, }); if (updatedProfile) { - notifications.show({ + showNotification({ title: 'Profile renamed', message: `${profile.name} → ${trimmedName}`, color: 'green.5', @@ -69,13 +77,13 @@ const ProfileModal = ({ opened, onClose, mode, profile }) => { } if (mode === 'duplicate') { - const duplicatedProfile = await API.duplicateChannelProfile( + const duplicatedProfile = await duplicateChannelProfile( profile.id, trimmedName ); if (duplicatedProfile) { - notifications.show({ + showNotification({ title: 'Profile duplicated', message: `${profile.name} copied to ${duplicatedProfile.name}`, color: 'green.5', diff --git a/frontend/src/hooks/useEpgPreview.jsx b/frontend/src/hooks/useEpgPreview.jsx index f2cab900..3c804ea3 100644 --- a/frontend/src/hooks/useEpgPreview.jsx +++ b/frontend/src/hooks/useEpgPreview.jsx @@ -1,6 +1,10 @@ import { useEffect, useState } from 'react'; import API from '../api'; +const getCurrentProgramForEpg = (epgId) => { + return API.getCurrentProgramForEpg(epgId); +}; + export const useEpgPreview = (epgDataId) => { const [currentProgram, setCurrentProgram] = useState(null); const [isLoadingProgram, setIsLoadingProgram] = useState(false); @@ -28,7 +32,7 @@ export const useEpgPreview = (epgDataId) => { if (cancelled || Date.now() - startTime > deadlineMs) break; try { - const program = await API.getCurrentProgramForEpg(epgDataId); + const program = await getCurrentProgramForEpg(epgDataId); if (cancelled) return; if (program && program.parsing && attempt < maxRetries) { diff --git a/frontend/src/pages/Connect.jsx b/frontend/src/pages/Connect.jsx index 099546af..293c2e57 100644 --- a/frontend/src/pages/Connect.jsx +++ b/frontend/src/pages/Connect.jsx @@ -18,6 +18,14 @@ import { SquarePlus, Webhook, FileCode, Logs } from 'lucide-react'; import ConnectionForm from '../components/forms/Connection'; import { SUBSCRIPTION_EVENTS } from '../constants'; +const deleteConnectIntegration = (id) => { + return API.deleteConnectIntegration(id); +}; + +const updateConnectIntegration = (id, values) => { + return API.updateConnectIntegration(id, values); +}; + export default function ConnectPage() { const { integrations, isLoading, fetchIntegrations } = useConnectStore(); const theme = useMantineTheme(); @@ -40,7 +48,7 @@ export default function ConnectPage() { const deleteConnection = async (id) => { console.log('Deleting connection', id); - await API.deleteConnectIntegration(id); + await deleteConnectIntegration(id); }; return ( @@ -99,15 +107,14 @@ function IntegrationRow({ integration, editConnection, deleteConnection }) { const toggleIntegration = async () => { try { - await API.updateConnectIntegration(integration.id, { + await updateConnectIntegration(integration.id, { ...integration, enabled: !enabled, }); setEnabled(!enabled); } catch (error) { console.error('Failed to update integration', error); - } finally { - } + } }; return ( diff --git a/frontend/src/pages/ConnectLogs.jsx b/frontend/src/pages/ConnectLogs.jsx index 08705a13..eccc3672 100644 --- a/frontend/src/pages/ConnectLogs.jsx +++ b/frontend/src/pages/ConnectLogs.jsx @@ -18,6 +18,10 @@ import { SUBSCRIPTION_EVENTS } from '../constants'; import { CustomTable, useTable } from '../components/tables/CustomTable'; import { copyToClipboard } from '../utils'; +const getConnectLogs = (params) => { + return API.getConnectLogs(params); +}; + export default function ConnectLogsPage() { const { integrations, fetchIntegrations } = useConnectStore(); @@ -51,7 +55,7 @@ export default function ConnectLogsPage() { if (filters.type) params.type = filters.type; if (filters.integration) params.integration = filters.integration; - const data = await API.getConnectLogs(params); + const data = await getConnectLogs(params); const results = Array.isArray(data) ? data : data?.results || []; setLogs(results); setCount(data?.count || results.length || 0);