diff --git a/frontend/src/components/forms/Channel.jsx b/frontend/src/components/forms/Channel.jsx index b4c494e1..53ecddd2 100644 --- a/frontend/src/components/forms/Channel.jsx +++ b/frontend/src/components/forms/Channel.jsx @@ -11,7 +11,6 @@ import logo from '../../images/logo.png'; import { useChannelLogoSelection } from '../../hooks/useSmartLogos'; import useLogosStore from '../../store/logos'; import LazyLogo from '../LazyLogo'; -import LogoForm from './Logo'; import { Box, Button, @@ -38,7 +37,7 @@ import { import { notifications } from '@mantine/notifications'; import { ListOrdered, SquarePlus, SquareX, X, Zap } from 'lucide-react'; import useEPGsStore from '../../store/epgs'; - +import { Dropzone } from '@mantine/dropzone'; import { FixedSizeList as List } from 'react-window'; import { USER_LEVELS, USER_LEVEL_LABELS } from '../../constants'; @@ -72,7 +71,7 @@ const ChannelForm = ({ channel = null, isOpen, onClose }) => { const tvgs = useEPGsStore((s) => s.tvgs); const tvgsById = useEPGsStore((s) => s.tvgsById); - const [logoModalOpen, setLogoModalOpen] = useState(false); + const [logoPreview, setLogoPreview] = useState(null); const [channelStreams, setChannelStreams] = useState([]); const [channelGroupModelOpen, setChannelGroupModalOpen] = useState(false); const [epgPopoverOpened, setEpgPopoverOpened] = useState(false); @@ -98,12 +97,33 @@ const ChannelForm = ({ channel = null, isOpen, onClose }) => { setChannelStreams(Array.from(streamSet)); }; - const handleLogoSuccess = ({ logo }) => { - if (logo && logo.id) { - formik.setFieldValue('logo_id', logo.id); - ensureLogosLoaded(); // Refresh logos + const handleLogoChange = async (files) => { + if (files.length === 1) { + const file = files[0]; + + // Validate file size on frontend first + if (file.size > 5 * 1024 * 1024) { + // 5MB + notifications.show({ + title: 'Error', + message: 'File too large. Maximum size is 5MB.', + color: 'red', + }); + return; + } + + try { + const retval = await API.uploadLogo(file); + // Note: API.uploadLogo already adds the logo to the store, no need to fetch + setLogoPreview(retval.cache_url); + formik.setFieldValue('logo_id', retval.id); + } catch (error) { + console.error('Logo upload failed:', error); + // Error notification is already handled in API.uploadLogo + } + } else { + setLogoPreview(null); } - setLogoModalOpen(false); }; const handleAutoMatchEpg = async () => { @@ -789,13 +809,35 @@ const ChannelForm = ({ channel = null, isOpen, onClose }) => { - + + + + OR + + + + + + Upload Logo + console.log('rejected files', files)} + maxSize={5 * 1024 ** 2} + > + + + Drag images here or click to select files + + + + +
+
@@ -1015,12 +1057,6 @@ const ChannelForm = ({ channel = null, isOpen, onClose }) => { isOpen={channelGroupModelOpen} onClose={handleChannelGroupModalClose} /> - - setLogoModalOpen(false)} - onSuccess={handleLogoSuccess} - /> ); }; diff --git a/frontend/src/components/forms/Logo.jsx b/frontend/src/components/forms/Logo.jsx index cb7f385e..25847ce8 100644 --- a/frontend/src/components/forms/Logo.jsx +++ b/frontend/src/components/forms/Logo.jsx @@ -106,12 +106,13 @@ const LogoForm = ({ logo = null, isOpen, onClose, onSuccess }) => { onSuccess?.({ type: 'create', logo: newLogo }); // Call onSuccess for creates } else { // File was uploaded and logo was already created + // Note: API.uploadLogo already calls addLogo() in the store, so no need to call onSuccess notifications.show({ title: 'Success', message: 'Logo uploaded successfully', color: 'green', }); - onSuccess?.({ type: 'create', logo: uploadResponse }); + // No onSuccess call needed - API.uploadLogo already updated the store } onClose(); } catch (error) {