import React, { CSSProperties } from 'react'; import { useNode, UserComponent } from '@craftjs/core'; import { cssPropsToString } from '../../utils/style-helpers'; import { AnchorIdField } from '../../ui/AnchorIdField'; interface FeatureItem { title: string; description: string; icon: string; mediaType?: 'icon' | 'image'; // default behaves as 'icon' when undefined image?: string; imageAlt?: string; buttonText?: string; buttonUrl?: string; } /* ---------- Image upload helper (same as Navbar/ImageBlock) ---------- */ async function uploadToWhp(file: File): Promise { const cfg = (window as any).WHP_CONFIG; if (!cfg) return URL.createObjectURL(file); const formData = new FormData(); formData.append('file', file); try { const resp = await fetch(`${cfg.apiUrl}?action=upload_asset&site_id=${cfg.siteId}`, { method: 'POST', headers: { 'X-CSRF-Token': cfg.csrfToken }, body: formData, }); const data = await resp.json(); if (data.success && data.url) return data.url; return null; } catch { return null; } } interface FeaturesGridProps { features?: FeatureItem[]; style?: CSSProperties; anchorId?: string; } const defaultFeatures: FeatureItem[] = [ { title: 'Fast & Reliable', description: 'Built for performance with optimized loading and rock-solid uptime.', icon: '⚡' }, { title: 'Easy to Use', description: 'Intuitive drag-and-drop interface that anyone can master in minutes.', icon: '✨' }, { title: 'Fully Responsive', description: 'Looks great on every device, from phones to ultrawide monitors.', icon: '📱' }, ]; export const FeaturesGrid: UserComponent = ({ features = defaultFeatures, style = {}, anchorId, }) => { const { connectors: { connect, drag }, selected, } = useNode((node) => ({ selected: node.events.selected, })); return (
{ if (ref) connect(drag(ref)); }} id={anchorId || undefined} style={{ padding: '80px 20px', backgroundColor: '#ffffff', outline: selected ? '2px solid #3b82f6' : 'none', ...style, }} >
{(Array.isArray(features) ? features : []).map((feat, i) => (
{feat.mediaType === 'image' && feat.image ? ( {feat.imageAlt ) : (
{feat.icon}
)}

{feat.title}

{feat.description}

{feat.buttonText ? ( e.preventDefault()} style={{ display: 'inline-block', marginTop: '16px', padding: '10px 24px', background: '#3b82f6', color: '#fff', borderRadius: '8px', textDecoration: 'none', fontSize: '14px', fontWeight: 600 }} > {feat.buttonText} ) : null}
))}
); }; /* ---------- Settings panel ---------- */ const FeaturesGridSettings: React.FC = () => { const { actions: { setProp }, props } = useNode((node) => ({ props: node.data.props as FeaturesGridProps, })); const features = props.features || defaultFeatures; const inputStyle: CSSProperties = { width: '100%', padding: '3px 6px', background: '#27272a', color: '#e4e4e7', border: '1px solid #3f3f46', borderRadius: 4, fontSize: 11, }; const updateFeature = (index: number, field: keyof FeatureItem, value: string) => { setProp((p: FeaturesGridProps) => { const updated = [...(p.features || defaultFeatures)]; updated[index] = { ...updated[index], [field]: value }; p.features = updated; }); }; const handleImageUpload = async (index: number, file: File) => { const url = await uploadToWhp(file); if (url) updateFeature(index, 'image', url); }; const addFeature = () => { setProp((p: FeaturesGridProps) => { p.features = [...(p.features || defaultFeatures), { title: 'New Feature', description: 'Describe this feature.', icon: '🔧' }]; }); }; const removeFeature = (index: number) => { setProp((p: FeaturesGridProps) => { const updated = [...(p.features || defaultFeatures)]; updated.splice(index, 1); p.features = updated; }); }; const bgPresets = ['#ffffff', '#f8fafc', '#f1f5f9', '#18181b', '#0f172a']; return (
{bgPresets.map((c) => (
{(Array.isArray(features) ? features : []).map((feat, i) => (
updateFeature(i, 'icon', e.target.value)} placeholder="Icon" style={{ ...inputStyle, width: 40, flex: 'none', textAlign: 'center' }} /> updateFeature(i, 'title', e.target.value)} placeholder="Title" style={{ ...inputStyle, flex: 1 }} />