Slight refactoring of components

This commit is contained in:
Nick Sandstrom 2026-07-14 14:18:53 -07:00
parent f6ad115a1f
commit 99fc71de99
7 changed files with 59 additions and 65 deletions

View file

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

View file

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

View file

@ -4,6 +4,7 @@ import {
Stack,
Text,
Radio,
RadioGroup,
NumberInput,
Checkbox,
Group,
@ -101,11 +102,7 @@ const CreateChannelModal = ({
<Divider label="Channel Number" labelPosition="left" />
<Radio.Group
value={mode}
onChange={onModeChange}
label={numberingLabel}
>
<RadioGroup value={mode} onChange={onModeChange} label={numberingLabel}>
<Stack mt="xs" spacing="xs">
<Radio
value="provider"
@ -148,7 +145,7 @@ const CreateChannelModal = ({
}
/>
</Stack>
</Radio.Group>
</RadioGroup>
{mode === customModeValue && (
<NumberInput

View file

@ -13,10 +13,18 @@ import {
} from '@mantine/core';
import { Copy, SquareMinus, SquarePen } from 'lucide-react';
import API from '../../api';
import { notifications } from '@mantine/notifications';
import { showNotification } from '../../utils/notificationUtils';
import useChannelsStore from '../../store/channels';
import { USER_LEVELS } from '../../constants';
const updateChannelProfile = (values) => {
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',

View file

@ -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) {

View file

@ -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 (

View file

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