import React, { useState, useEffect, useRef } from 'react'; import { NodeViewWrapper } from '@tiptap/react'; import { Globe, Edit2, Save, X, AlignLeft, AlignCenter, AlignRight, Trash } from 'lucide-react'; import { useEditorProvider } from '@components/Contexts/Editor/EditorContext'; import { getUrlPreview } from '@services/courses/activities'; interface EditorContext { isEditable: boolean; [key: string]: any; } interface WebPreviewProps { node: any; updateAttributes: (attrs: any) => void; extension: any; deleteNode?: () => void; } const ALIGNMENTS = [ { value: 'left', label: }, { value: 'center', label: }, { value: 'right', label: }, ]; const WebPreviewComponent: React.FC = ({ node, updateAttributes, deleteNode }) => { const [inputUrl, setInputUrl] = useState(node.attrs.url || ''); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [editing, setEditing] = useState(!node.attrs.url); const inputRef = useRef(null); const editorContext = useEditorProvider() as EditorContext; const isEditable = editorContext?.isEditable ?? true; const previewData = { title: node.attrs.title, description: node.attrs.description, og_image: node.attrs.og_image, favicon: node.attrs.favicon, og_type: node.attrs.og_type, og_url: node.attrs.og_url, url: node.attrs.url, }; const alignment = node.attrs.alignment || 'left'; const hasPreview = !!previewData.title; const [buttonLabel, setButtonLabel] = useState(node.attrs.buttonLabel || 'Visit Site'); const [showButton, setShowButton] = useState(node.attrs.showButton !== false); const fetchPreview = async (url: string) => { setLoading(true); setError(null); try { const res = await getUrlPreview(url); if (!res) throw new Error('Failed to fetch preview'); const data = res; updateAttributes({ ...data, url }); setEditing(false); } catch (err: any) { setError(err.message || 'Error fetching preview'); } finally { setLoading(false); } }; useEffect(() => { if (node.attrs.url && !hasPreview) { fetchPreview(node.attrs.url); } // eslint-disable-next-line }, []); useEffect(() => { if (editing && inputRef.current) { inputRef.current.focus(); } }, [editing]); useEffect(() => { setButtonLabel(node.attrs.buttonLabel || 'Visit Site'); setShowButton(!!node.attrs.showButton); }, [node.attrs.buttonLabel, node.attrs.showButton]); const handleAlignmentChange = (value: string) => { updateAttributes({ alignment: value }); }; const handleEdit = () => { setEditing(true); setInputUrl(node.attrs.url || ''); }; const handleSaveEdit = () => { if (inputUrl && inputUrl !== node.attrs.url) { fetchPreview(inputUrl); } else { setEditing(false); } updateAttributes({ buttonLabel, showButton }); }; const handleCancelEdit = () => { setEditing(false); setInputUrl(node.attrs.url || ''); setError(null); }; const handleDelete = () => { if (typeof deleteNode === 'function') { deleteNode(); } else { updateAttributes({ url: null, title: null, description: null, og_image: null, favicon: null, og_type: null, og_url: null }); } }; // Compute alignment class for CardWrapper let alignClass = 'justify-start'; if (alignment === 'center') alignClass = 'justify-center'; else if (alignment === 'right') alignClass = 'justify-end'; return (
{/* CardWrapper */}
{/* PreviewCard */} {/* Floating edit and delete buttons (only if not editing and isEditable) */} {isEditable && !editing && (
)} {/* Only show edit bar when editing */} {isEditable && editing && ( <>
{/* EditBar */} setInputUrl(e.target.value)} disabled={loading} onKeyDown={e => { if (e.key === 'Enter') handleSaveEdit(); }} className="flex-1 border border-gray-200 rounded-md px-2.5 py-1.5 text-sm font-sans focus:outline-none focus:border-gray-400" />
{/* Button toggle and label input */}
{showButton && ( { setButtonLabel(e.target.value); updateAttributes({ buttonLabel: e.target.value }); }} placeholder="Button label" className="border border-gray-200 rounded-md px-2 py-1 text-sm font-sans focus:outline-none focus:border-gray-400" style={{ minWidth: 100 }} /> )}
)} {error &&
{error}
} {/* Only show preview card when not editing */} {hasPreview && !editing && ( <> {previewData.og_image && (
preview
)}
{previewData.favicon && ( favicon )} {previewData.url}
{showButton && previewData.url && ( {buttonLabel || 'Visit Site'} )} )} {isEditable && !editing && (
{/* AlignmentBar */} Align: {ALIGNMENTS.map(opt => ( ))}
)}
); }; export default WebPreviewComponent;