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);