diff --git a/frontend/src/components/tables/EPGsTable.jsx b/frontend/src/components/tables/EPGsTable.jsx index c429e3c4..e9afe24e 100644 --- a/frontend/src/components/tables/EPGsTable.jsx +++ b/frontend/src/components/tables/EPGsTable.jsx @@ -24,6 +24,8 @@ import { RefreshCcw, SquareMinus, SquarePen } from 'lucide-react'; import dayjs from 'dayjs'; import useSettingsStore from '../../store/settings'; import useLocalStorage from '../../hooks/useLocalStorage'; +import ConfirmationDialog from '../../components/ConfirmationDialog'; +import useWarningsStore from '../../store/warnings'; // Helper function to format status text const formatStatusText = (status) => { @@ -47,6 +49,9 @@ const EPGsTable = () => { const [epg, setEPG] = useState(null); const [epgModalOpen, setEPGModalOpen] = useState(false); const [rowSelection, setRowSelection] = useState([]); + const [confirmDeleteOpen, setConfirmDeleteOpen] = useState(false); + const [deleteTarget, setDeleteTarget] = useState(null); + const [epgToDelete, setEpgToDelete] = useState(null); const epgs = useEPGsStore((s) => s.epgs); const refreshProgress = useEPGsStore((s) => s.refreshProgress); @@ -61,6 +66,9 @@ const EPGsTable = () => { // Calculate density for Mantine Table const tableDensity = tableSize === 'compact' ? 'xs' : tableSize === 'large' ? 'xl' : 'md'; + const isWarningSuppressed = useWarningsStore((s) => s.isWarningSuppressed); + const suppressWarning = useWarningsStore((s) => s.suppressWarning); + const toggleActive = async (epg) => { try { // Send only the is_active field to trigger our special handling @@ -245,9 +253,24 @@ const EPGsTable = () => { }; const deleteEPG = async (id) => { + // Get EPG details for the confirmation dialog + const epgObj = epgs[id]; + setEpgToDelete(epgObj); + setDeleteTarget(id); + + // Skip warning if it's been suppressed + if (isWarningSuppressed('delete-epg')) { + return executeDeleteEPG(id); + } + + setConfirmDeleteOpen(true); + }; + + const executeDeleteEPG = async (id) => { setIsLoading(true); await API.deleteEPG(id); setIsLoading(false); + setConfirmDeleteOpen(false); }; const refreshEPG = async (id) => { @@ -446,6 +469,36 @@ const EPGsTable = () => { + + setConfirmDeleteOpen(false)} + onConfirm={() => executeDeleteEPG(deleteTarget)} + title="Confirm EPG Source Deletion" + message={ + epgToDelete ? ( +
+ {`Are you sure you want to delete the following EPG source? + +Name: ${epgToDelete.name} +Source Type: ${epgToDelete.source_type} +${epgToDelete.url ? `URL: ${epgToDelete.url}` : + epgToDelete.api_key ? `API Key: ${epgToDelete.api_key}` : + epgToDelete.file_path ? `File Path: ${epgToDelete.file_path}` : ''} + +This will remove all related program information and channel associations. +This action cannot be undone.`} +
+ ) : ( + 'Are you sure you want to delete this EPG source? This action cannot be undone.' + ) + } + confirmLabel="Delete" + cancelLabel="Cancel" + actionKey="delete-epg" + onSuppressChange={suppressWarning} + size="lg" + /> ); }; diff --git a/frontend/src/components/tables/M3UsTable.jsx b/frontend/src/components/tables/M3UsTable.jsx index e2b38f0d..0c5b2b4d 100644 --- a/frontend/src/components/tables/M3UsTable.jsx +++ b/frontend/src/components/tables/M3UsTable.jsx @@ -24,6 +24,8 @@ import { IconSquarePlus } from '@tabler/icons-react'; // Import custom icons import dayjs from 'dayjs'; import useSettingsStore from '../../store/settings'; import useLocalStorage from '../../hooks/useLocalStorage'; +import ConfirmationDialog from '../../components/ConfirmationDialog'; +import useWarningsStore from '../../store/warnings'; // Helper function to format status text const formatStatusText = (status) => { @@ -58,12 +60,17 @@ const M3UTable = () => { const [rowSelection, setRowSelection] = useState([]); const [activeFilterValue, setActiveFilterValue] = useState('all'); const [playlistCreated, setPlaylistCreated] = useState(false); + const [confirmDeleteOpen, setConfirmDeleteOpen] = useState(false); + const [deleteTarget, setDeleteTarget] = useState(null); + const [playlistToDelete, setPlaylistToDelete] = useState(null); const playlists = usePlaylistsStore((s) => s.playlists); const refreshProgress = usePlaylistsStore((s) => s.refreshProgress); const setRefreshProgress = usePlaylistsStore((s) => s.setRefreshProgress); const editPlaylistId = usePlaylistsStore((s) => s.editPlaylistId); const setEditPlaylistId = usePlaylistsStore((s) => s.setEditPlaylistId); + const isWarningSuppressed = useWarningsStore((s) => s.isWarningSuppressed); + const suppressWarning = useWarningsStore((s) => s.suppressWarning); const theme = useMantineTheme(); const [tableSize] = useLocalStorage('table-size', 'default'); @@ -274,9 +281,24 @@ const M3UTable = () => { }; const deletePlaylist = async (id) => { + // Get playlist details for the confirmation dialog + const playlist = playlists.find(p => p.id === id); + setPlaylistToDelete(playlist); + setDeleteTarget(id); + + // Skip warning if it's been suppressed + if (isWarningSuppressed('delete-m3u')) { + return executeDeletePlaylist(id); + } + + setConfirmDeleteOpen(true); + }; + + const executeDeletePlaylist = async (id) => { setIsLoading(true); await API.deletePlaylist(id); setIsLoading(false); + setConfirmDeleteOpen(false); }; const toggleActive = async (playlist) => { @@ -688,6 +710,34 @@ const M3UTable = () => { onClose={closeModal} playlistCreated={playlistCreated} /> + + setConfirmDeleteOpen(false)} + onConfirm={() => executeDeletePlaylist(deleteTarget)} + title="Confirm M3U Account Deletion" + message={ + playlistToDelete ? ( +
+ {`Are you sure you want to delete the following M3U account? + +Name: ${playlistToDelete.name} +Type: ${playlistToDelete.account_type === 'XC' ? 'Xtream Codes' : 'Standard'} +Server: ${playlistToDelete.server_url || 'Local file'} + +This will remove all related streams and may affect channels using these streams. +This action cannot be undone.`} +
+ ) : ( + 'Are you sure you want to delete this M3U account? This action cannot be undone.' + ) + } + confirmLabel="Delete" + cancelLabel="Cancel" + actionKey="delete-m3u" + onSuppressChange={suppressWarning} + size="lg" + /> ); };