diff --git a/apps/web/components/Objects/Editor/Extensions/WebPreview/WebPreview.ts b/apps/web/components/Objects/Editor/Extensions/WebPreview/WebPreview.ts index f37923d6..27c36189 100644 --- a/apps/web/components/Objects/Editor/Extensions/WebPreview/WebPreview.ts +++ b/apps/web/components/Objects/Editor/Extensions/WebPreview/WebPreview.ts @@ -19,6 +19,7 @@ const WebPreview = Node.create({ alignment: { default: 'left' }, buttonLabel: { default: 'Visit Site' }, showButton: { default: false }, + openInPopup: { default: false }, } }, diff --git a/apps/web/components/Objects/Editor/Extensions/WebPreview/WebPreviewComponent.tsx b/apps/web/components/Objects/Editor/Extensions/WebPreview/WebPreviewComponent.tsx index 80de0bc5..2290d7af 100644 --- a/apps/web/components/Objects/Editor/Extensions/WebPreview/WebPreviewComponent.tsx +++ b/apps/web/components/Objects/Editor/Extensions/WebPreview/WebPreviewComponent.tsx @@ -3,6 +3,11 @@ 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; @@ -46,6 +51,9 @@ const WebPreviewComponent: React.FC = ({ node, updateAttributes 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); @@ -79,7 +87,15 @@ const WebPreviewComponent: React.FC = ({ node, updateAttributes useEffect(() => { setButtonLabel(node.attrs.buttonLabel || 'Visit Site'); setShowButton(!!node.attrs.showButton); - }, [node.attrs.buttonLabel, 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 }); @@ -88,6 +104,7 @@ const WebPreviewComponent: React.FC = ({ node, updateAttributes const handleEdit = () => { setEditing(true); setInputUrl(node.attrs.url || ''); + setModalOpen(true); }; const handleSaveEdit = () => { @@ -95,14 +112,17 @@ const WebPreviewComponent: React.FC = ({ node, updateAttributes fetchPreview(inputUrl); } else { setEditing(false); + setModalOpen(false); } - updateAttributes({ buttonLabel, showButton }); + updateAttributes({ buttonLabel, showButton, openInPopup }); + setModalOpen(false); }; const handleCancelEdit = () => { setEditing(false); setInputUrl(node.attrs.url || ''); setError(null); + setModalOpen(false); }; const handleDelete = () => { @@ -120,6 +140,23 @@ const WebPreviewComponent: React.FC = ({ node, updateAttributes return ( + {/* Popup Modal for Embedded Website */} + + } + />
{/* CardWrapper */}
{/* PreviewCard */} {/* Floating edit and delete buttons (only if not editing and isEditable) */} @@ -143,71 +180,98 @@ const WebPreviewComponent: React.FC = ({ node, updateAttributes
)} - {/* 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 && ( - { + 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(); }}> +
+ + { - 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 }} + placeholder="Enter website URL..." + value={inputUrl} + onChange={e => setInputUrl(e.target.value)} + disabled={loading} + autoFocus /> - )} -
- - )} - {error &&
{error}
} +
+
+ +
+
+ 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 && ( <> @@ -261,39 +325,53 @@ const WebPreviewComponent: React.FC = ({ node, updateAttributes {previewData.url}
{showButton && previewData.url && ( - - {buttonLabel || 'Visit Site'} - + openInPopup ? ( + + ) : ( + + {buttonLabel || 'Visit Site'} + + ) + )} + {/* Alignment bar in view mode */} + {isEditable && ( +
+
{/* AlignmentBar */} + Align: + {ALIGNMENTS.map(opt => ( + + ))} +
+
)} )} - {isEditable && !editing && ( -
{/* AlignmentBar */} - Align: - {ALIGNMENTS.map(opt => ( - - ))} -
- )}