'use client'; /** * IPTVSourceManager - Admin UI to manage M3U playlist sources */ import { useState } from 'react'; import { useIPTVStore, type IPTVSource } from '@/lib/store/iptv-store'; import { Icons } from '@/components/ui/Icon'; import { hasPermission } from '@/lib/store/auth-store'; const inputProps = { spellCheck: false, autoCorrect: 'off' as const, autoCapitalize: 'off' as const, autoComplete: 'off' as const, 'data-form-type': 'other', 'data-lpignore': 'true', lang: 'en', translate: 'no' as const, }; export function IPTVSourceManager() { const { sources, addSource, removeSource, updateSource, refreshSources, isLoading } = useIPTVStore(); const canUseBuiltinSources = hasPermission('iptv_builtin_sources'); const [name, setName] = useState(''); const [url, setUrl] = useState(''); const [showAdd, setShowAdd] = useState(false); const [editingId, setEditingId] = useState(null); const [editName, setEditName] = useState(''); const [editUrl, setEditUrl] = useState(''); const visibleSources = sources.filter((source) => canUseBuiltinSources || source.kind !== 'builtin'); const handleAdd = () => { if (!name.trim() || !url.trim()) return; addSource(name.trim(), url.trim()); setName(''); setUrl(''); setShowAdd(false); // Auto-refresh after adding (only if not already loading) if (!isLoading) { setTimeout(() => refreshSources(), 100); } }; const startEdit = (source: IPTVSource) => { setEditingId(source.id); setEditName(source.name); setEditUrl(source.url); }; const cancelEdit = () => { setEditingId(null); setEditName(''); setEditUrl(''); }; const saveEdit = () => { if (!editingId || !editName.trim() || !editUrl.trim()) return; updateSource(editingId, { name: editName.trim(), url: editUrl.trim() }); setEditingId(null); setEditName(''); setEditUrl(''); // Auto-refresh after editing if (!isLoading) { setTimeout(() => refreshSources(), 100); } }; return (

直播源管理

{/* Add Source Form */} {showAdd && (
setName(e.target.value)} {...inputProps} className="w-full px-3 py-2 bg-[var(--glass-bg)] border border-[var(--glass-border)] rounded-[var(--radius-2xl)] text-sm text-[var(--text-color)] placeholder:text-[var(--text-color-secondary)]/50 focus:outline-none focus:border-[var(--accent-color)]" /> setUrl(e.target.value)} {...inputProps} className="w-full px-3 py-2 bg-[var(--glass-bg)] border border-[var(--glass-border)] rounded-[var(--radius-2xl)] text-sm text-[var(--text-color)] placeholder:text-[var(--text-color-secondary)]/50 focus:outline-none focus:border-[var(--accent-color)]" />
)} {/* Source List */} {visibleSources.length === 0 ? (
暂无直播源,请添加 M3U 或 JSON 播放列表链接
) : (
{visibleSources.map((source) => (
{editingId === source.id ? ( /* Edit Mode */
setEditName(e.target.value)} {...inputProps} className="w-full px-3 py-1.5 bg-[var(--glass-bg)] border border-[var(--glass-border)] rounded-[var(--radius-2xl)] text-sm text-[var(--text-color)] focus:outline-none focus:border-[var(--accent-color)]" /> setEditUrl(e.target.value)} {...inputProps} className="w-full px-3 py-1.5 bg-[var(--glass-bg)] border border-[var(--glass-border)] rounded-[var(--radius-2xl)] text-sm text-[var(--text-color)] focus:outline-none focus:border-[var(--accent-color)]" />
) : ( /* Display Mode */

{source.name}

{source.kind === 'builtin' && ( 内置 )}

{source.url}

{source.kind !== 'builtin' ? ( <> ) : ( 环境变量提供 )}
)}
))}
)}
); }