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'; import Modal from '@components/Objects/StyledElements/Modal/Modal'; import { Input } from '@components/ui/input'; import { Label } from '@components/ui/label'; import { Checkbox } from '@components/ui/checkbox'; import { Button } from '@components/ui/button'; 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 [openInPopup, setOpenInPopup] = useState(node.attrs.openInPopup || false); const [popupOpen, setPopupOpen] = useState(false); const [modalOpen, setModalOpen] = useState(!node.attrs.url); 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); setOpenInPopup(!!node.attrs.openInPopup); }, [node.attrs.buttonLabel, node.attrs.showButton, node.attrs.openInPopup]); useEffect(() => { if (!node.attrs.url) { setEditing(true); setModalOpen(true); } }, [node.attrs.url]); const handleAlignmentChange = (value: string) => { updateAttributes({ alignment: value }); }; const handleEdit = () => { setEditing(true); setInputUrl(node.attrs.url || ''); setModalOpen(true); }; const handleSaveEdit = () => { if (inputUrl && inputUrl !== node.attrs.url) { fetchPreview(inputUrl); } else { setEditing(false); setModalOpen(false); } updateAttributes({ buttonLabel, showButton, openInPopup }); setModalOpen(false); }; const handleCancelEdit = () => { setEditing(false); setInputUrl(node.attrs.url || ''); setError(null); setModalOpen(false); }; 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 ( {/* Popup Modal for Embedded Website */} } />
{/* CardWrapper */}
{/* PreviewCard */} {/* Floating edit and delete buttons (only if not editing and isEditable) */} {isEditable && !editing && (
)} {/* Modal for editing */} { setModalOpen(open); if (!open) handleCancelEdit(); }} dialogTitle="Edit Web Preview Card" dialogDescription="Update the website preview, button, and display options." minWidth="md" dialogContent={
{ e.preventDefault(); handleSaveEdit(); }}>
setInputUrl(e.target.value)} disabled={loading} autoFocus />
setShowButton(!!checked)} />
{showButton && ( <>
setOpenInPopup(!!checked)} />
setButtonLabel(e.target.value)} placeholder="Button label" className="w-36" />
)}
{ALIGNMENTS.map(opt => ( ))}
{error &&
{error}
}
} /> {/* Only show preview card when not editing */} {hasPreview && !editing && ( <> {previewData.og_image && (
preview
)}
{previewData.favicon && ( favicon )} {previewData.url}
{showButton && previewData.url && ( openInPopup ? ( ) : ( {buttonLabel || 'Visit Site'} ) )} {/* Alignment bar in view mode */} {isEditable && (
{/* AlignmentBar */} Align: {ALIGNMENTS.map(opt => ( ))}
)} )}
); }; export default WebPreviewComponent;