"use client"; import { useState, useEffect } from "react"; import { useAuth } from "@/contexts/AuthContext"; import { contentVideosApi, ContentVideo, ContentVideoCreate } from "@/lib/api"; import { Youtube, Plus, Trash2, Edit, GripVertical, X, ExternalLink, Eye, EyeOff, } from "lucide-react"; interface YouTubeVideoManagerProps { entityType: "project" | "solution" | "product"; entityId: number; entityTitle?: string; } export default function YouTubeVideoManager({ entityType, entityId, entityTitle, }: YouTubeVideoManagerProps) { const { token } = useAuth(); const [videos, setVideos] = useState([]); const [loading, setLoading] = useState(true); const [showModal, setShowModal] = useState(false); const [editingVideo, setEditingVideo] = useState(null); const [formData, setFormData] = useState>({ youtube_id: "", title_ko: "", title_en: "", title_ja: "", title_zh: "", description_ko: "", entity_type: entityType, entity_id: entityId, is_active: true, }); const fetchVideos = async () => { if (!token) return; try { const data = await contentVideosApi.adminList(token, entityType, entityId); setVideos(data); } catch (error) { console.error("Failed to fetch videos:", error); } finally { setLoading(false); } }; useEffect(() => { fetchVideos(); }, [token, entityType, entityId]); const extractYoutubeId = (input: string): string => { if (!input) return ""; // Already a plain ID if (/^[a-zA-Z0-9_-]{11}$/.test(input)) return input; // Extract from URL const match = input.match( /(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([a-zA-Z0-9_-]{11})/ ); return match ? match[1] : input; }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!token) return; try { const youtubeId = extractYoutubeId(formData.youtube_id || ""); const payload = { ...formData, youtube_id: youtubeId, entity_type: entityType, entity_id: entityId, }; if (editingVideo) { await contentVideosApi.adminUpdate(editingVideo.id, payload, token); } else { await contentVideosApi.adminCreate(payload as ContentVideoCreate, token); } setShowModal(false); setEditingVideo(null); resetForm(); fetchVideos(); } catch (error) { console.error("Failed to save video:", error); alert("저장에 실패했습니다."); } }; const handleDelete = async (id: number) => { if (!token || !confirm("이 영상을 삭제하시겠습니까?")) return; try { await contentVideosApi.adminDelete(id, token); fetchVideos(); } catch (error) { console.error("Failed to delete video:", error); } }; const handleToggleActive = async (video: ContentVideo) => { if (!token) return; try { await contentVideosApi.adminUpdate( video.id, { is_active: !video.is_active }, token ); fetchVideos(); } catch (error) { console.error("Failed to toggle active:", error); } }; const resetForm = () => { setFormData({ youtube_id: "", title_ko: "", title_en: "", title_ja: "", title_zh: "", description_ko: "", entity_type: entityType, entity_id: entityId, is_active: true, }); }; const openEditModal = (video: ContentVideo) => { setEditingVideo(video); setFormData({ youtube_id: video.youtube_id, title_ko: video.title_ko, title_en: video.title_en || "", title_ja: video.title_ja || "", title_zh: video.title_zh || "", description_ko: video.description_ko || "", is_active: video.is_active, }); setShowModal(true); }; const openAddModal = () => { setEditingVideo(null); resetForm(); setShowModal(true); }; return (

YouTube 영상

{entityTitle && ( - {entityTitle} )}
{loading ? (
로딩 중...
) : videos.length === 0 ? (

등록된 영상이 없습니다

) : (
{videos.map((video) => (
{/* Thumbnail */}
{video.title_ko} { (e.target as HTMLImageElement).src = `https://img.youtube.com/vi/${video.youtube_id}/hqdefault.jpg`; }} />
{/* Info */}

{video.title_ko}

{video.title_en && (

{video.title_en}

)}

ID: {video.youtube_id}

{/* Actions */}
))}
)} {/* Modal */} {showModal && (

{editingVideo ? "영상 수정" : "영상 추가"}

{/* YouTube URL/ID */}
setFormData({ ...formData, youtube_id: e.target.value }) } placeholder="https://youtube.com/watch?v=... 또는 영상 ID" className="w-full px-3 py-2 border rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent" required />

전체 URL이나 영상 ID(11자리)를 입력하세요

{/* Preview */} {formData.youtube_id && (
Preview { (e.target as HTMLImageElement).src = `https://img.youtube.com/vi/${extractYoutubeId( formData.youtube_id || "" )}/hqdefault.jpg`; }} />
)} {/* Title KO */}
setFormData({ ...formData, title_ko: e.target.value }) } className="w-full px-3 py-2 border rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent" required />
{/* Title EN */}
setFormData({ ...formData, title_en: e.target.value }) } className="w-full px-3 py-2 border rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent" />
{/* Title JA */}
setFormData({ ...formData, title_ja: e.target.value }) } className="w-full px-3 py-2 border rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent" />
{/* Title ZH */}
setFormData({ ...formData, title_zh: e.target.value }) } className="w-full px-3 py-2 border rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent" />
{/* Description */}