| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135 |
- import * as React from 'react';
- // FIX: Import FormBlock and FormSubmission to handle form rendering and data submission.
- import { PageSettings, ThemeName, Block, SocialPlatform, HeaderBlock, DesignSettings, ButtonShape, ButtonStyle, FontFamily, BackgroundType, LinkBlock, VideoBlock, ImageBlock, EmailBlock, PhoneBlock, BannerSettings, MediaSource, TextBlock, AIGCVideo, SocialBlock, ChatMessage, ChatWidgetSettings, PdfBlock, MapBlock, SideNavSettings, NewsBlock, AIGCArticle, ProductBlock, ChatBlock, EnterpriseInfoBlock, EnterpriseInfoIcon, FormBlock, FormSubmission, AwardBlock, FooterBlock } from '../types';
- import { Icon } from './ui/Icon';
- import ChatWidget from './ChatWidget';
- import { sendChatMessage } from '../services/geminiService';
- import EnterpriseNav from './EnterpriseNav';
- import { format, parseISO } from 'date-fns';
- // --- STYLING HELPERS ---
- const getImageUrl = (source?: MediaSource): string | undefined => {
- if (!source) return undefined;
- switch (source.type) {
- case 'url': return source.value;
- case 'file': return source.value.previewUrl;
- default: return undefined;
- }
- };
- var themePresets: Record<ThemeName, { bg: string, text: string, button: string, buttonText: string }> = {
- light: { bg: '#F3F4F6', text: '#1F2937', button: '#FFFFFF', buttonText: '#1F2937' },
- dark: { bg: '#1F2937', text: '#FFFFFF', button: '#374151', buttonText: '#FFFFFF' },
- synthwave: { bg: '#2D3748', text: '#F0D43A', button: '#EC4899', buttonText: '#FFFFFF' },
- retro: { bg: '#FEF3C7', text: '#854D0E', button: '#FBBF24', buttonText: '#000000' },
- custom: { bg: '', text: '', button: '', buttonText: '' } // Handled separately
- };
- var socialIconPaths: Record<SocialPlatform, React.ReactElement> = {
- twitter: React.createElement("path", { d: "M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.223.085a4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z" }),
- instagram: React.createElement("path", { d: "M12 2.163c3.204 0 3.584.012 4.85.07 1.17.055 1.805.248 2.227.415.562.217.96.477 1.382.896.413.42.67.824.896 1.383.167.422.36 1.057.413 2.227.057 1.266.07 1.646.07 4.85s-.013 3.583-.07 4.85c-.054 1.17-.246 1.805-.413 2.227-.228.562-.483.96-.896 1.383-.42.413-.824-.67-1.382-.896-.422-.167-1.057.36-2.227.413-1.266.057-1.646.07-4.85.07s-3.585-.013-4.85-.07c-1.17-.054-1.805-.246-2.227-.413-.562-.228-.96-.483-1.382-.896-.413-.42-.67-.824-.896-1.383-.167-.422-.36-1.057-.413-2.227-.057-1.266-.07-1.646-.07-4.85s.013-3.585.07-4.85c.054-1.17.246 1.805.413-2.227.228.562.483.96.896-1.382.42-.413.824-.67 1.382-.896.422-.167 1.057.36 2.227-.413 1.265-.057 1.646-.07 4.85-.07M12 0C8.74 0 8.333.014 7.053.072 5.775.132 4.905.333 4.14.63c-.784.3-1.2.623-1.77.933-.448.174-.9.34-1.356.59-.446.243-.823.533-1.116.823-.293.29-.58.67-.823 1.116-.25.456-.417.908-.59 1.356-.31.77-.63 1.2-.933 1.77-.298.765-.498 1.635-.558 2.913-.058 1.28-.072 1.687-.072 4.947s.014 3.667.072 4.947c.06 1.278.26 2.148.558 2.913.3.77.623 1.2.933 1.77.174.448.34.9.59 1.356.243.446.533.823.823 1.116.29.293.67.58 1.116.823.456.25.908.417 1.356.59.77.31 1.2.63 1.77.933.765.298 1.635.498 2.913.558 1.28.058 1.687.072 4.947.072s3.667-.014 4.947-.072c1.278-.06 2.148-.26 2.913-.558.77-.3 1.2-.623 1.77-.933.448-.174.9-.34 1.356.59.446-.243.823-.533 1.116.823.293-.29.58-.67.823-1.116.25-.456-.417-.908-.59-1.356.31-.77.63-1.2.933-1.77.298-.765.498 1.635.558-2.913.058-1.28.072-1.687.072-4.947s-.014-3.667-.072-4.947c-.06-1.278-.26-2.148-.558-2.913-.3-.77-.623-1.2-.933-1.77-.174-.448-.34-.9-.59-1.356-.243-.446-.533-.823-.823-1.116-.29-.293-.67-.58-1.116-.823-.456-.25-.908-.417-1.356-.59-.77-.31-1.2-.63-1.77-.933C16.947.333 16.077.132 14.797.072 13.517.014 13.11 0 12 0zm0 5.838a6.162 6.162 0 100 12.324 6.162 6.162 0 000-12.324zM12 16a4 4 0 110-8 4 4 0 010 8zm6.406-11.845a1.44 1.44 0 100 2.88 1.44 1.44 0 000-2.88z" }),
- facebook: React.createElement("path", { d: "M22.675 0H1.325C.593 0 0 .593 0 1.325v21.351C0 23.407.593 24 1.325 24H12.82v-9.294H9.692v-3.622h3.128V8.413c0-3.1 1.893-4.788 4.659-4.788 1.325 0 2.463.099 2.795.143v3.24l-1.918.001c-1.504 0-1.795.715-1.795 1.763v2.313h3.587l-.467 3.622h-3.12V24h6.116c.732 0 1.325-.593 1.325-1.325V1.325C24 .593 23.407 0 22.675 0z" }),
- linkedin: React.createElement("path", { d: "M19 0h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.762 0 5-2.239 5-5v-14c0-2.761-2.238-5-5-5zm-11 19h-3v-11h3v11zm-1.5-12.268c-.966 0-1.75-.79-1.75-1.764s.784-1.764 1.75-1.764 1.75.79 1.75 1.764-.783 1.764-1.75 1.764zm13.5 12.268h-3v-5.604c0-3.368-4-3.113-4 0v5.604h-3v-11h3v1.765c1.396-2.586 7-2.777 7 2.476v6.759z" }),
- youtube: React.createElement("path", { d: "M21.582 6.186A2.69 2.69 0 0019.54 4.54a49.123 49.123 0 00-7.54-.38A49.123 49.123 0 004.46 4.54a2.69 2.69 0 00-2.042 1.646A28.024 28.024 0 002.31 12a28.024 28.024 0 00.108 5.814A2.69 2.69 0 004.46 19.46a49.123 4.9123 0 007.54.38 49.123 49.123 0 007.54-.38a2.69 2.69 0 002.042-1.646A28.024 28.024 0 0021.69 12a28.024 28.024 0 00-.108-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z" }),
- tiktok: React.createElement("path", { d: "M12.525.02c1.31-.02 2.61-.01 3.91.02.08 1.53.63 3.09 1.75 4.17 1.12 1.11 2.7 1.65 4.31 1.72v3.28c-1.31.08-2.62.06-3.92-.01-1.25-.07-2.47-.62-3.38-1.5-1.2-1.15-1.85-2.76-1.9-4.48v5.72c0 2.55-2.09 4.63-4.66 4.63-2.58 0-4.66-2.09-4.66-4.66s2.08-4.66 4.66-4.66c.33 0 .66.04.98.11v3.24c-1.31-.08-2.62-.06-3.92.01-1.25.07-2.47.62-3.38 1.5-1.2 1.15-1.85 2.76-1.9 4.48v-2.32c0-2.55 2.09-4.63 4.66-4.63 2.58 0 4.66 2.09 4.66 4.66s-2.08 4.66-4.66 4.66c-.33 0-.66-.04-.98-.11C6.25 19.38 5.14 18.25 4.2 17.29v-5.72c0-2.55 2.09-4.63 4.66-4.63.1 0 .2.01.29.02z" }),
- github: React.createElement("path", { d: "M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12" })
- };
- const enterpriseInfoIcons: Record<EnterpriseInfoIcon, React.ReactElement> = {
- building: <path strokeLinecap="round" strokeLinejoin="round" d="M3.75 21h16.5M4.5 3h15M5.25 3v18m13.5-18v18M9 6.75h6.375a.375.375 0 01.375.375v1.5a.375.375 0 01-.375.375H9a.375.375 0 01-.375-.375v-1.5A.375.375 0 019 6.75zM9 12.75h6.375a.375.375 0 01.375.375v1.5a.375.375 0 01-.375.375H9a.375.375 0 01-.375-.375v-1.5A.375.375 0 019 12.75z" />,
- bank: <path strokeLinecap="round" strokeLinejoin="round" d="M12 21v-8.25M15.75 21v-8.25M8.25 21v-8.25M3 9l9-6 9 6m-1.5 12V5.625A2.625 2.625 0 0017.25 3H6.75A2.625 2.625 0 004.5 5.625V21" />,
- money: <path strokeLinecap="round" strokeLinejoin="round" d="M2.25 18.75a60.07 60.07 0 0115.797 2.101c.727.198 1.453-.342 1.453-1.096V18.75M3.75 4.5v.75A.75.75 0 013 6h-.75m0 0v-.375c0-.621.504-1.125 1.125-1.125H20.25M2.25 6v9m18-10.5v.75c0 .414.336.75.75.75h.75m-1.5-1.5h.375c.621 0 1.125.504 1.125 1.125v9.75c0 .621-.504 1.125-1.125 1.125h-15c-.621 0-1.125-.504-1.125-1.125v-9.75c0-.621.504-1.125 1.125-1.125h.375m15-1.5v-1.5A.75.75 0 0020.25 4.5h-15a.75.75 0 00-.75.75v1.5m15 0a.75.75 0 01-.75.75H4.5a.75.75 0 01-.75-.75m15 0v.375c0 .621-.504 1.125-1.125 1.125h-15.75c-.621 0-1.125-.504-1.125-1.125v-.375m15.75 0v3.472a60.1 60.1 0 01-15.797-2.102c-.727-.198-1.453.342-1.453 1.096V4.5" />,
- location: React.createElement(React.Fragment, {}, React.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M15 10.5a3 3 0 11-6 0 3 3 0 616 0z" }), React.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19.5 10.5c0 7.142-7.5 11.25-7.5 11.25S4.5 17.642 4.5 10.5a7.5 7.5 0 1115 0z" })),
- calendar: <path strokeLinecap="round" strokeLinejoin="round" d="M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 012.25-2.25h13.5A2.25 2.25 0 0121 7.5v11.25m-18 0A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75m-18 0h18M12 15h.008v.008H12V15zm0 2.25h.008v.008H12v-.008zm2.25-2.25h.008v.008H14.25v-.008zm0 2.25h.008v.008H14.25v-.008zm2.25-2.25h.008v.008H16.5v-.008zm0 2.25h.008v.008H16.5v-.008zm-4.5-2.25h.008v.008H9.75v-.008zm0 2.25h.008v.008H9.75v-.008z" />,
- users: <path strokeLinecap="round" strokeLinejoin="round" d="M15 19.128a9.38 9.38 0 002.625.372 9.337 9.337 0 004.121-.952 4.125 4.125 0 00-7.53-2.493M15 19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 018.624 21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0111.964-4.663M12 12a4.5 4.5 0 100-9 4.5 4.5 0 000 9z" />,
- lightbulb: <path strokeLinecap="round" strokeLinejoin="round" d="M12 18v-5.25m0 0a3 3 0 00-3-3 3 3 0 00-3 3 3 3 0 003 3zm0 0c-1.125 0-2.25.5-2.25 1.5V18m0-5.25c1.125 0 2.25.5 2.25 1.5V18M12 3a9 9 0 100 18 9 9 0 000-18z" />,
- };
- var getFontClass = function(font: FontFamily) {
- switch(font) {
- case 'serif': return 'font-serif';
- case 'mono': return 'font-mono';
- case 'sans': default: return 'font-sans';
- }
- };
- const getFontFamilyValue = (font: FontFamily) => {
- switch (font) {
- case 'serif': return 'serif';
- case 'mono': return 'monospace';
- case 'sans': default: return 'sans-serif';
- }
- }
- var getButtonClasses = function(shape: ButtonShape, style: ButtonStyle) {
- var classes = 'w-full p-3 font-semibold transition-transform transform hover:scale-105 shadow-md flex items-center justify-center gap-4 text-center ';
- if(shape === 'pill') classes += 'rounded-full';
- else if(shape === 'square') classes += 'rounded-none';
- else classes += 'rounded-lg';
- if(style === 'outline') classes += ' bg-transparent border-2';
- return classes;
- };
- var getBackgroundStyle = function(type: BackgroundType, value: string) {
- if (type === 'image') return { backgroundImage: "url(" + value + ")", backgroundSize: 'cover', backgroundPosition: 'center' };
- if (type === 'color') return { backgroundColor: value };
- return {}; // for gradient, we use a class
- };
- var getBackgroundClass = function(type: BackgroundType, value: string) {
- return type === 'gradient' ? value : '';
- };
- var getBannerStyle = function(settings: BannerSettings): React.CSSProperties {
- var style: React.CSSProperties = { height: settings.height + "px" };
- const imageUrl = getImageUrl(settings.imageSource);
- if (settings.type === 'image' && imageUrl) {
- style.backgroundImage = "url(" + imageUrl + ")";
- style.backgroundSize = 'cover';
- style.backgroundPosition = 'center';
- }
- if (settings.type === 'color') {
- style = Object.assign({}, style, { backgroundColor: settings.value });
- }
- return style;
- };
- var getBannerClass = function(settings: BannerSettings) {
- var classes = '';
- if (settings.type === 'gradient') {
- classes += " " + settings.value;
- }
- return classes;
- };
- // --- Child Components ---
- var ActionModal: React.FC<{
- data: { type: 'email' | 'phone'; value: string; label: string; } | null;
- onClose: () => void;
- }> = function(props) {
- var data = props.data, onClose = props.onClose;
- if (!data) return null;
-
- var isEmail = data.type === 'email';
- var actionText = isEmail ? 'Open Mail App' : 'Call';
- var actionHref = isEmail ? "mailto:" + data.value : "tel:" + data.value;
- var handleCopy = function() {
- navigator.clipboard.writeText(data.value).then(function() {
- alert((isEmail ? 'Email' : 'Number') + " copied to clipboard!");
- onClose();
- });
- };
- return (
- React.createElement("div", { className: "absolute inset-0 bg-black/50 flex items-center justify-center z-[100]", onClick: () => onClose() } as any,
- React.createElement("div", { className: "bg-white dark:bg-gray-800 rounded-lg shadow-xl w-full max-w-xs p-6 text-center", onClick: function(e) { e.stopPropagation(); } } as any,
- React.createElement("h3", { className: "font-bold text-lg mb-2 text-gray-900 dark:text-white" }, data.label),
- React.createElement("p", { className: "text-gray-600 dark:text-gray-300 mb-6 break-words" }, data.value),
- React.createElement("div", { className: "space-y-3" },
- React.createElement("button", { onClick: handleCopy, className: "w-full bg-brand-primary text-white font-semibold py-3 rounded-lg" }, "Copy " + (isEmail ? 'Email' : 'Number')),
- React.createElement("a", { href: actionHref, className: "block w-full bg-gray-200 dark:bg-gray-700 text-gray-800 dark:text-gray-200 font-semibold py-3 rounded-lg" }, actionText),
- React.createElement("button", { onClick: () => onClose(), className: "mt-4 w-full text-sm text-gray-500 dark:text-gray-400" } as any, "Cancel")
- )
- )
- )
- );
- };
- var PersonalChatOverlay: React.FC<{ isOpen: boolean; onClose: () => void; settings: ChatWidgetSettings }> = function(props) {
- var isOpen = props.isOpen, onClose = props.onClose, settings = props.settings;
- var messagesState = React.useState<ChatMessage[]>([]);
- var messages = messagesState[0];
- var setMessages = messagesState[1];
- var userInputState = React.useState('');
- var userInput = userInputState[0];
- var setUserInput = userInputState[1];
- var isLoadingState = React.useState(false);
- var isLoading = isLoadingState[0];
- var setIsLoading = isLoadingState[1];
- var isRecordingState = React.useState(false);
- var isRecording = isRecordingState[0];
- var setIsRecording = isRecordingState[1];
- var messagesEndRef = React.useRef<HTMLDivElement>(null);
- var recognitionRef = React.useRef<any>(null);
- React.useEffect(function() {
- // @ts-ignore
- var SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
- if (SpeechRecognition) {
- recognitionRef.current = new SpeechRecognition();
- recognitionRef.current.continuous = false;
- recognitionRef.current.lang = 'en-US';
- recognitionRef.current.interimResults = false;
- recognitionRef.current.maxAlternatives = 1;
- recognitionRef.current.onresult = function(event: any) {
- var transcript = event.results[0][0].transcript;
- setUserInput(transcript);
- setIsRecording(false);
- };
- recognitionRef.current.onerror = function(event: any) {
- console.error('Speech recognition error:', event.error);
- setIsRecording(false);
- };
-
- recognitionRef.current.onend = function() {
- setIsRecording(false);
- };
- }
- }, []);
- React.useEffect(function() {
- if (isOpen && messages.length === 0) {
- var initialMessage: ChatMessage = { id: "msg-ai-" + Date.now(), sender: 'ai', text: 'Hello! How can I help you today?' };
- setMessages([initialMessage]);
- }
- }, [isOpen, messages.length]);
- React.useEffect(function() {
- if (messagesEndRef.current) {
- messagesEndRef.current.scrollIntoView({ behavior: 'smooth' });
- }
- }, [messages, isLoading]);
- var handleSendMessage = React.useCallback(function() {
- if (!userInput.trim()) return;
- var userMessage: ChatMessage = { id: "msg-user-" + Date.now(), sender: 'user', text: userInput };
- setMessages(function(prev) { return prev.concat([userMessage]); });
- var messageToSend = userInput;
- setUserInput('');
- setIsLoading(true);
- sendChatMessage(messageToSend).then(function(aiResponse) {
- setMessages(function(prev) { return prev.concat([aiResponse]); });
- setIsLoading(false);
- });
- }, [userInput]);
-
- var toggleRecording = function() {
- if (!recognitionRef.current) {
- alert("Speech recognition is not supported by your browser.");
- return;
- }
- if (isRecording) {
- recognitionRef.current.stop();
- } else {
- recognitionRef.current.start();
- }
- setIsRecording(!isRecording);
- };
- return React.createElement("div", {
- className: "absolute inset-0 bg-black/30 z-50 transition-opacity duration-300 " + (isOpen ? "opacity-100" : "opacity-0 pointer-events-none"),
- onClick: function() { return onClose(); }
- } as any,
- React.createElement("div", {
- className: "absolute bottom-0 left-0 right-0 h-3/4 bg-white dark:bg-gray-800 rounded-t-2xl shadow-2xl flex flex-col transition-transform duration-300 " + (isOpen ? "translate-y-0" : "translate-y-full"),
- style: { backgroundColor: settings.panelBackgroundColor },
- onClick: function(e) { e.stopPropagation(); }
- } as any,
- React.createElement("div", { className: "flex-shrink-0 p-4 flex justify-between items-center rounded-t-2xl", style: { backgroundColor: settings.headerBackgroundColor, color: settings.headerTextColor } } as any,
- React.createElement("h3", { className: "font-bold text-lg" }, "AI Assistant"),
- React.createElement("button", { onClick: () => onClose(), style: { color: settings.headerTextColor } } as any,
- React.createElement(Icon, { className: "h-6 w-6", children: React.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 9l-7 7-7-7" }) })
- )
- ),
- React.createElement("div", { className: "flex-1 p-4 overflow-y-auto space-y-4" } as any,
- messages.map(function(msg) {
- return React.createElement("div", { key: msg.id, className: "flex items-start gap-3 " + (msg.sender === 'user' ? 'justify-end' : '') },
- msg.sender === 'ai' && React.createElement("div", { className: "w-8 h-8 rounded-full flex items-center justify-center flex-shrink-0", style: { backgroundColor: settings.aiMessageBackgroundColor, color: settings.aiMessageTextColor } }, "🤖"),
- React.createElement("div", { className: "max-w-xs p-3 rounded-lg text-sm", style: { backgroundColor: msg.sender === 'user' ? settings.userMessageBackgroundColor : settings.aiMessageBackgroundColor, color: msg.sender === 'user' ? settings.userMessageTextColor : settings.aiMessageTextColor, borderRadius: msg.sender === 'user' ? '1rem 1rem 0.25rem 1rem' : '1rem 1rem 1rem 0.25rem' } } as any,
- React.createElement("p", null, msg.text)
- )
- );
- }),
- isLoading && React.createElement("div", { className: "flex items-start gap-3" },
- React.createElement("div", { className: "w-8 h-8 rounded-full flex items-center justify-center flex-shrink-0", style: { backgroundColor: settings.aiMessageBackgroundColor } }, "🤖"),
- React.createElement("div", { className: "max-w-xs p-3 rounded-lg", style: { backgroundColor: settings.aiMessageBackgroundColor } } as any,
- React.createElement("div", { className: "flex items-center space-x-1" },
- React.createElement("span", { className: "w-2 h-2 rounded-full animate-pulse delay-0", style: { backgroundColor: settings.aiMessageTextColor } }),
- React.createElement("span", { className: "w-2 h-2 rounded-full animate-pulse delay-150", style: { backgroundColor: settings.aiMessageTextColor } }),
- React.createElement("span", { className: "w-2 h-2 rounded-full animate-pulse delay-300", style: { backgroundColor: settings.aiMessageTextColor } })
- )
- )
- ),
- React.createElement("div", { ref: messagesEndRef })
- ),
- React.createElement("div", { className: "flex-shrink-0 p-4 border-t border-gray-200 dark:border-gray-700 flex items-center gap-3" } as any,
- React.createElement("button", { onClick: toggleRecording, className: "p-2 rounded-full " + (isRecording ? "bg-red-500 animate-pulse" : "bg-gray-200 dark:bg-gray-700") } as any,
- React.createElement(Icon, { className: "h-6 w-6 text-gray-800 dark:text-white", children: React.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 11a7 7 0 01-7 7m0 0a7 7 0 01-7-7m7 7v4m0 0H8m4 0h4m-4-8a3 3 0 01-3-3V5a3 3 0 116 0v6a3 3 0 01-3 3z" }) })
- ),
- React.createElement("input", { type: "text", value: userInput, onChange: function(e) { return setUserInput(e.target.value); }, onKeyPress: function(e) { return e.key === 'Enter' && !isLoading && handleSendMessage(); }, placeholder: "Ask me anything...", className: "flex-1 bg-gray-100 dark:bg-gray-700 text-gray-900 dark:text-white p-2 rounded-md border border-gray-300 dark:border-gray-600 focus:ring-2 focus:ring-brand-primary focus:outline-none", disabled: isLoading } as any),
- React.createElement("button", { onClick: handleSendMessage, disabled: isLoading, className: "p-2 rounded-full text-white disabled:opacity-50", style: { backgroundColor: settings.headerBackgroundColor } } as any,
- React.createElement(Icon, { className: "h-6 w-6", children: React.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M5 10l7-7m0 0l7 7m-7-7v18" }) })
- )
- )
- )
- );
- };
- // --- MAIN PREVIEW COMPONENT ---
- interface PreviewProps {
- pageSettings: PageSettings;
- aigcVideos: AIGCVideo[];
- aigcArticles: AIGCArticle[];
- previewMode: 'personal' | 'enterprise';
- deviceView: 'pc' | 'mobile';
- onFormSubmit: (submission: FormSubmission) => void;
- }
- const FormBlockComponent: React.FC<{
- block: FormBlock;
- theme: { text: string; button: string; buttonText: string };
- onFormSubmit: (submission: FormSubmission) => void;
- }> = ({ block, theme, onFormSubmit }) => {
- const [formData, setFormData] = React.useState<Record<string, string>>({});
- const inputClasses = "w-full p-2 rounded-md bg-white/10 border border-white/20 focus:outline-none focus:ring-1 focus:ring-white";
- const handleInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) => {
- setFormData({
- ...formData,
- [e.target.name]: e.target.value,
- });
- };
- const handleSubmit = (e: React.FormEvent) => {
- e.preventDefault();
- const submission: FormSubmission = {
- id: `sub_${Date.now()}`,
- formId: block.id,
- visitorId: 'visitor_123', // mock visitor id
- timestamp: new Date().toISOString(),
- data: formData,
- };
- onFormSubmit(submission);
- // Reset form
- const emptyData: Record<string, string> = {};
- block.fields.filter(f => f.enabled).forEach(f => emptyData[f.id] = '');
- if (block.purposeOptions.length > 0) {
- emptyData['purpose'] = block.purposeOptions[0].label;
- }
- setFormData(emptyData);
- };
- React.useEffect(() => {
- const initialData: Record<string, string> = {};
- if (block.purposeOptions.length > 0) {
- initialData['purpose'] = block.purposeOptions[0].label;
- }
- setFormData(initialData);
- }, [block.purposeOptions]);
- return (
- <div style={{ backgroundColor: 'rgba(128,128,128,0.1)', color: theme.text }} className="p-6 rounded-lg">
- <h3 className="text-xl font-bold mb-2 text-center" style={{ color: theme.text }}>{block.title}</h3>
- <p className="text-sm opacity-80 mb-6 text-center" style={{ color: theme.text }}>{block.description}</p>
- <form onSubmit={handleSubmit} className="space-y-4">
- {block.fields.filter(f => f.enabled).map(field => (
- <div key={field.id} className="flex items-center gap-4">
- <label htmlFor={field.id} className="w-1/3 text-sm font-medium" style={{ color: theme.text }}>
- {field.label} {field.required && <span className="text-red-500">*</span>}
- </label>
- <input
- type={field.id === 'email' ? 'email' : field.id === 'phone' ? 'tel' : 'text'}
- id={field.id}
- name={field.id}
- required={field.required}
- onChange={handleInputChange}
- value={formData[field.id] || ''}
- className={`${inputClasses} flex-1`}
- style={{color: theme.text, backgroundColor: 'rgba(255,255,255,0.1)'}}
- />
- </div>
- ))}
- {block.purposeOptions.length > 0 && (
- <div className="flex items-center gap-4">
- <label htmlFor="purpose" className="w-1/3 text-sm font-medium" style={{ color: theme.text }}>
- Purpose
- </label>
- <select
- id="purpose"
- name="purpose"
- onChange={handleInputChange}
- value={formData['purpose'] || block.purposeOptions[0].label}
- className={`${inputClasses} flex-1`}
- style={{color: theme.text, backgroundColor: 'rgba(255,255,255,0.1)'}}
- >
- {block.purposeOptions.map(opt => <option key={opt.id} value={opt.label} style={{backgroundColor: '#374151'}}>{opt.label}</option>)}
- </select>
- </div>
- )}
- <button type="submit" style={{ backgroundColor: theme.button, color: theme.buttonText }} className="w-full p-3 font-semibold rounded-lg">
- {block.submitButtonText}
- </button>
- </form>
- </div>
- );
- };
- export var Preview: React.FC<PreviewProps> = function(props) {
- var { pageSettings, aigcVideos, aigcArticles, previewMode, deviceView, onFormSubmit } = props;
- var design = pageSettings.design;
- var blocks = pageSettings.blocks;
- var theme = design.theme === 'custom'
- ? {
- bg: design.customThemeColors.background,
- text: design.customThemeColors.text,
- button: design.customThemeColors.button,
- buttonText: design.customThemeColors.buttonText
- }
- : themePresets[design.theme];
-
- if (design.fontColor) {
- theme.text = design.fontColor;
- }
- var actionModalDataState = React.useState<{ type: 'email' | 'phone'; value: string; label: string; } | null>(null);
- var actionModalData = actionModalDataState[0];
- var setActionModalData = actionModalDataState[1];
-
- var isChatOpenState = React.useState(false);
- var isChatOpen = isChatOpenState[0];
- var setIsChatOpen = isChatOpenState[1];
-
- var scrollContainerRef = React.useRef<HTMLDivElement>(null);
- const chatBlock = blocks.find(b => b.type === 'chat' && b.visible) as ChatBlock | undefined;
- const handleViewGeneratedArticle = (article: AIGCArticle) => {
- const isDark = design.theme === 'dark' || design.theme === 'synthwave';
- const styles = `
- body {
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
- line-height: 1.6;
- color: ${isDark ? '#e5e7eb' : '#1f2937'};
- background-color: ${isDark ? '#111827' : '#ffffff'};
- max-width: 800px;
- margin: 40px auto;
- padding: 20px;
- }
- .prose { max-width: 65ch; margin: 0 auto; }
- .prose h1, .prose h2, .prose h3 { color: inherit; }
- .prose a { color: #10b981; }
- .prose img { max-width: 100%; height: auto; border-radius: 8px; }
- .prose .lead { font-size: 1.25em; color: ${isDark ? '#9ca3af' : '#6b7280'}; }
- `;
-
- const articleHtml = `
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>${article.title}</title>
- <style>${styles}</style>
- </head>
- <body>
- <article class="prose">
- <h1>${article.title}</h1>
- <p class="lead">${article.summary}</p>
- <hr>
- <div>${article.content}</div>
- </article>
- </body>
- </html>
- `;
- const newWindow = window.open();
- if (newWindow) {
- newWindow.document.write(articleHtml);
- newWindow.document.close();
- }
- };
- React.useEffect(function() {
- if (scrollContainerRef.current) {
- scrollContainerRef.current.scrollTop = 0;
- }
- }, [previewMode, deviceView]);
- React.useEffect(function() {
- if (isChatOpen) {
- document.body.style.overflow = 'hidden';
- } else {
- document.body.style.overflow = 'unset';
- }
- return function() {
- document.body.style.overflow = 'unset';
- };
- }, [isChatOpen]);
- var visibleBlocks = blocks.filter(function(b) { return b.visible; });
-
- var renderBlock = function(block: Block) {
- var isMobile = deviceView === 'mobile';
- const lineClampStyle = (lines: number): React.CSSProperties => ({
- display: '-webkit-box',
- WebkitLineClamp: lines,
- WebkitBoxOrient: 'vertical',
- overflow: 'hidden',
- textOverflow: 'ellipsis',
- });
- switch (block.type) {
- case 'header':
- var headerBlock = block as HeaderBlock;
- return React.createElement("h2", { className: "text-2xl font-bold pt-4", style: { color: theme.text, textAlign: headerBlock.titleAlignment } }, headerBlock.text);
-
- case 'link':
- var linkBlock = block as LinkBlock;
- return (
- React.createElement("a", { href: linkBlock.url, target: "_blank", rel: "noopener noreferrer", className: getButtonClasses(design.buttonShape, design.buttonStyle), style: { backgroundColor: design.buttonStyle === 'filled' ? theme.button : 'transparent', color: theme.buttonText, borderColor: theme.buttonText } },
- linkBlock.iconUrl && React.createElement("img", { src: linkBlock.iconUrl, alt: "", className: "w-6 h-6 rounded-md" }),
- React.createElement("span", { className: "flex-1" }, linkBlock.title)
- )
- );
- case 'chat': {
- const currentChatBlock = block as ChatBlock;
- if (previewMode === 'personal' && currentChatBlock.layout !== 'button') return null;
- if (currentChatBlock.layout === 'widget') return null;
-
- const chatButtonStyle: React.CSSProperties = {};
- if (design.buttonStyle === 'filled') {
- chatButtonStyle.backgroundColor = '#10b981';
- chatButtonStyle.color = '#ffffff';
- } else {
- chatButtonStyle.backgroundColor = 'transparent';
- chatButtonStyle.color = '#10b981';
- chatButtonStyle.borderColor = '#10b981';
- }
- return React.createElement("button", {
- onClick: () => setIsChatOpen(true),
- className: getButtonClasses(design.buttonShape, design.buttonStyle) + ' h-16',
- style: chatButtonStyle
- } as any, "Chat with me");
- }
- case 'social':
- var socialBlock = block as SocialBlock;
- return (
- React.createElement("div", { className: "flex justify-center gap-4" },
- socialBlock.links.map(function(link) {
- var icon = socialIconPaths[link.platform];
- return React.createElement("a", { key: link.id, href: link.url, target: "_blank", rel: "noopener noreferrer", style: { color: theme.text }, className: "hover:opacity-75" },
- React.createElement("svg", {
- className: "h-8 w-8",
- fill: "currentColor",
- viewBox: "0 0 24 24"
- }, icon)
- );
- })
- )
- );
- case 'video':
- var videoBlock = block as VideoBlock;
- var videoLayoutClass = videoBlock.layout === 'grid' ? 'grid grid-cols-2 gap-2' : 'space-y-2';
- return (
- React.createElement("div", { className: videoLayoutClass },
- videoBlock.sources.map(function(source, index) {
- var video = source.type === 'aigc' ? aigcVideos.filter(function(v) { return v.id === source.videoId; })[0] : null;
- var url = source.type === 'url' ? source.value : (video && video.videoUrl);
- if (!url) return null;
- return React.createElement("video", { key: index, src: url, controls: true, className: "w-full rounded-lg aspect-video" });
- })
- )
- );
- case 'image':
- var imageBlock = block as ImageBlock;
- var imageLayoutClass = imageBlock.layout === 'grid' ? 'grid grid-cols-2 gap-2' : 'space-y-2';
- return (
- React.createElement("div", { className: imageLayoutClass },
- imageBlock.sources.map(function(source, index) {
- var url = source.type === 'url' ? source.value : (source.type === 'file' ? source.value.previewUrl : '');
- return React.createElement("img", { key: index, src: url, alt: "", className: "w-full rounded-lg object-cover aspect-video" });
- })
- )
- );
- case 'text':
- var textBlock = block as TextBlock;
- return (
- React.createElement("p", { style: {
- color: textBlock.fontColor || theme.text,
- fontSize: textBlock.fontSize,
- textAlign: textBlock.textAlign,
- fontWeight: textBlock.isBold ? 'bold' : 'normal',
- fontStyle: textBlock.isItalic ? 'italic' : 'normal'
- } },
- textBlock.content
- )
- );
- case 'enterprise_info': {
- const infoBlock = block as EnterpriseInfoBlock;
- if (!infoBlock.items || infoBlock.items.length === 0) return null;
- const isCentered = infoBlock.alignment === 'center';
- const containerClasses = isCentered ? 'flex flex-col items-center text-center gap-6' : 'space-y-4';
- const itemClasses = isCentered ? 'flex flex-col items-center gap-2' : 'flex items-start gap-4';
- return (
- React.createElement("div", { className: `p-4 rounded-lg`, style: { backgroundColor: 'rgba(128,128,128,0.1)' } },
- React.createElement("div", { className: containerClasses },
- infoBlock.items.map(item => (
- React.createElement("div", { key: item.id, className: itemClasses },
- React.createElement("div", { className: `flex-shrink-0 w-10 h-10 flex items-center justify-center rounded-lg`, style: { backgroundColor: 'rgba(128,128,128,0.1)', color: theme.text } },
- React.createElement(Icon, { className: "h-6 w-6", children: enterpriseInfoIcons[item.icon] })
- ),
- React.createElement("div", { className: "min-w-0" },
- React.createElement("p", { className: "text-sm font-semibold uppercase tracking-wider", style: { color: theme.text, opacity: 0.7 } }, item.label),
- React.createElement("p", { className: "font-semibold text-base break-words", style: { color: theme.text } }, item.value)
- )
- )
- ))
- )
- )
- );
- }
- case 'news': {
- const newsBlock = block as NewsBlock;
- const isGrid = newsBlock.layout === 'grid';
- const containerClass = isGrid ? 'grid grid-cols-1 md:grid-cols-2 gap-4' : 'space-y-4';
-
- const extractFirstImageUrl = (htmlContent: string): string | null => {
- if (typeof DOMParser === 'undefined') return null;
- try {
- const doc = new DOMParser().parseFromString(htmlContent, 'text/html');
- const img = doc.querySelector('img');
- return img ? img.src : null;
- } catch (e) {
- console.error("Error parsing HTML for image extraction", e);
- return null;
- }
- };
-
- if (newsBlock.source === 'custom') {
- const customItems = newsBlock.customItems || [];
- if (customItems.length === 0) return null;
-
- return React.createElement("div", { className: containerClass },
- customItems.map(item => (
- React.createElement("a", {
- key: item.id,
- href: item.url,
- target: "_blank",
- rel: "noopener noreferrer",
- className: `block w-full text-left p-4 rounded-lg transition-shadow hover:shadow-lg bg-gray-100 dark:bg-gray-800 border border-gray-200 dark:border-gray-700`
- },
- React.createElement("h3", { className: "font-bold text-lg", style: { color: theme.text } }, item.title),
- React.createElement("p", { className: "text-sm text-brand-primary truncate mt-1" }, item.url),
- React.createElement("p", { className: "text-sm mt-2", style: { color: theme.text, opacity: 0.9 } }, item.summary)
- )
- ))
- );
- }
-
- const articlesToShow = (newsBlock.articleIds || [])
- .map(id => aigcArticles.find(a => a.id === id))
- .filter((a): a is AIGCArticle => !!a);
-
- if (articlesToShow.length === 0) return null;
-
- return React.createElement("div", { className: containerClass },
- articlesToShow.map(article => {
- const imageUrl = extractFirstImageUrl(article.content);
- const hasUrl = article.sourceType === 'url' && article.sourceUrl;
-
- const linkProps: any = {
- key: article.id,
- className: `block w-full text-left rounded-lg transition-shadow hover:shadow-lg bg-gray-100 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 overflow-hidden`,
- };
- if (hasUrl) {
- linkProps.href = article.sourceUrl;
- linkProps.target = "_blank";
- linkProps.rel = "noopener noreferrer";
- } else {
- linkProps.href = "#";
- linkProps.onClick = (e: React.MouseEvent) => {
- e.preventDefault();
- handleViewGeneratedArticle(article);
- };
- }
-
- const articleContent = isGrid ? (
- React.createElement(React.Fragment, null,
- imageUrl && React.createElement("img", { src: imageUrl, alt: article.title, className: "w-full h-32 object-cover" }),
- React.createElement("div", { className: "p-4" },
- React.createElement("h3", { className: "font-bold text-lg", style: { color: theme.text } }, article.title),
- React.createElement("p", { className: "text-sm text-gray-600 dark:text-gray-400 mt-1" }, format(parseISO(article.publicationDate), 'MMM d, yyyy')),
- React.createElement("p", { className: "text-sm mt-2", style: { color: theme.text, opacity: 0.9 } }, article.summary)
- )
- )
- ) : ( // List View
- React.createElement("div", { className: "flex items-start gap-4 p-4" },
- imageUrl && React.createElement("img", { src: imageUrl, alt: article.title, className: "w-40 h-24 object-cover rounded-md flex-shrink-0" }),
- React.createElement("div", { className: "flex-1 min-w-0" },
- React.createElement("h3", { className: "font-bold text-lg", style: { ...{ color: theme.text }, ...lineClampStyle(2) } }, article.title),
- React.createElement("p", { className: "text-sm text-gray-600 dark:text-gray-400 mt-1" }, format(parseISO(article.publicationDate), 'MMM d, yyyy')),
- React.createElement("p", { className: "text-sm mt-2", style: { ...{ color: theme.text, opacity: 0.9 }, ...lineClampStyle(2) } }, article.summary)
- )
- )
- );
- return React.createElement("a", linkProps, articleContent);
- })
- );
- }
- case 'email':
- case 'phone':
- var actionBlock = block as EmailBlock | PhoneBlock;
- var iconPath = actionBlock.type === 'email' ?
- React.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" }) :
- React.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" });
- var buttonContent = React.createElement("div", {
- className: "flex flex-col items-center justify-center text-center"
- },
- React.createElement("div", { className: "flex items-center gap-2" },
- React.createElement(Icon, { className: "h-5 w-5 flex-shrink-0", children: iconPath }),
- React.createElement("span", null, actionBlock.label)
- ),
- actionBlock.displayMode === 'labelAndValue' && React.createElement("span", {
- className: "text-xs opacity-80 mt-1 font-normal"
- }, actionBlock.type === 'email' ? actionBlock.email : actionBlock.phone)
- );
- return React.createElement("button", {
- onClick: function() { return setActionModalData({ type: actionBlock.type, value: actionBlock.type === 'email' ? actionBlock.email : actionBlock.phone, label: actionBlock.label }); },
- className: getButtonClasses(design.buttonShape, design.buttonStyle),
- style: {
- backgroundColor: design.buttonStyle === 'filled' ? theme.button : 'transparent',
- color: theme.buttonText,
- borderColor: theme.buttonText
- }
- } as any, buttonContent);
-
- case 'map':
- var mapBlock = block as MapBlock;
- var encodedAddress = encodeURIComponent(mapBlock.address);
- var displayStyle = mapBlock.displayStyle || 'interactiveMap';
-
- if (displayStyle === 'imageOverlay') {
- var imageUrl = getImageUrl(mapBlock.backgroundImageSource);
- return React.createElement("a", {
- href: "https://www.google.com/maps/search/?api=1&query=" + encodedAddress,
- target: "_blank",
- rel: "noopener noreferrer",
- className: "block rounded-lg overflow-hidden relative h-48 group"
- },
- React.createElement("div", {
- className: "absolute inset-0 bg-cover bg-center transition-transform duration-300 group-hover:scale-105",
- style: { backgroundImage: "url(" + (imageUrl || ("https://picsum.photos/seed/mapbg-" + mapBlock.id + "/600/400")) + ")" }
- }),
- React.createElement("div", {
- className: "absolute inset-0 bg-black/60 flex items-end p-4"
- },
- React.createElement("p", { className: "text-white font-semibold" }, mapBlock.address)
- )
- );
- }
-
- return React.createElement("a", {
- href: "https://www.google.com/maps/search/?api=1&query=" + encodedAddress,
- target: "_blank",
- rel: "noopener noreferrer",
- className: "block rounded-lg overflow-hidden border border-gray-200 dark:border-gray-700 hover:border-brand-primary transition-colors"
- },
- React.createElement("div", {
- className: "relative h-48 bg-cover bg-center",
- style: { backgroundImage: "url(https://picsum.photos/seed/map" + mapBlock.id + "/600/400)" }
- },
- React.createElement("div", { className: "absolute inset-0 bg-black/50 flex items-end p-4" },
- React.createElement("p", { className: "text-white font-semibold" }, mapBlock.address)
- )
- )
- );
-
- case 'pdf':
- var pdfBlock = block as PdfBlock;
- var source = pdfBlock.source;
- var url: string | undefined;
- var name: string | undefined;
- var previewUrl: string | undefined;
- if (source.type === 'file') {
- url = '#';
- name = source.value.name;
- previewUrl = source.value.previewUrl;
- } else if (source.type === 'url') {
- url = source.value;
- try {
- var urlParts = new URL(url).pathname.split('/');
- name = urlParts[urlParts.length - 1] || "Document.pdf";
- } catch (e) {
- name = "Document.pdf";
- }
- previewUrl = 'https://api.iconify.design/mdi:file-pdf-box.svg?color=%23' + (theme.text.replace('#',''));
- } else {
- return null;
- }
-
- var isFile = source.type === 'file';
- var content = React.createElement("div", { className: "flex items-center gap-4" },
- React.createElement("img", { src: previewUrl, className: "w-16 h-20 object-contain rounded-md flex-shrink-0 bg-gray-200 dark:bg-gray-600 p-2" }),
- React.createElement("div", { className: "flex-1 min-w-0" },
- React.createElement("p", { className: "font-semibold truncate", style: { color: theme.text } }, name),
- !isFile && url && React.createElement("p", { className: "text-sm text-brand-primary" }, "View PDF")
- )
- );
- if (!isFile && url) {
- return React.createElement("a", { href: url, target: "_blank", rel: "noopener noreferrer", className: "block bg-gray-100 dark:bg-gray-700/50 p-4 rounded-lg border border-gray-200 dark:border-gray-700 hover:border-brand-primary" }, content);
- } else {
- return React.createElement("div", { className: "bg-gray-100 dark:bg-gray-700/50 p-4 rounded-lg border border-gray-200 dark:border-gray-700" }, content);
- }
-
- case 'product': {
- const productBlock = block as ProductBlock;
- if (productBlock.items.length === 0) return null;
-
- const isGrid = productBlock.layout === 'grid';
- const containerClass = isGrid ? 'grid grid-cols-2 gap-4' : 'space-y-4';
-
- const cardClass = `block w-full text-left rounded-lg transition-shadow hover:shadow-lg overflow-hidden border`;
- const cardBgStyle = {
- backgroundColor: theme.button,
- borderColor: design.buttonStyle === 'outline' ? theme.buttonText : 'transparent',
- };
-
- return (
- React.createElement("div", { className: containerClass },
- productBlock.items.map(item => (
- React.createElement("a", {
- key: item.id,
- href: item.url,
- target: "_blank",
- rel: "noopener noreferrer",
- className: cardClass,
- style: cardBgStyle
- },
- React.createElement("img", { src: item.imageUrl, alt: item.title, className: `w-full object-cover ${isGrid ? 'aspect-square' : 'aspect-video'}` }),
- React.createElement("div", { className: "p-4" },
- React.createElement("h3", { className: "font-bold", style: { color: theme.buttonText } }, item.title),
- React.createElement("p", { className: "font-semibold mt-1", style: { color: theme.buttonText, opacity: 0.9 } }, item.price)
- )
- )
- ))
- )
- );
- }
- case 'award': {
- const awardBlock = block as AwardBlock;
- if (!awardBlock.items || awardBlock.items.length === 0) return null;
- const isGrid = awardBlock.layout === 'grid';
- const containerClass = isGrid ? `grid ${isMobile ? 'grid-cols-1' : 'grid-cols-2'} gap-4` : 'space-y-4';
- return (
- React.createElement("div", { className: containerClass },
- awardBlock.items.map(item => {
- const imageUrl = getImageUrl(item.imageSource);
- if (isGrid) {
- return (
- React.createElement("div", { key: item.id, className: "flex flex-col items-center text-center gap-3 p-6 rounded-lg shadow-sm", style: { backgroundColor: 'rgba(128,128,128,0.1)' } },
- imageUrl && (
- React.createElement("div", { className: "p-1 rounded-full bg-gradient-to-br from-yellow-300 via-amber-400 to-orange-500 flex-shrink-0" },
- React.createElement("img", { src: imageUrl, alt: item.title, className: "w-24 h-24 rounded-full object-cover border-4 border-transparent", style: { borderColor: theme.bg } })
- )
- ),
- React.createElement("div", { className: "flex-1 min-w-0" },
- item.year && (
- React.createElement("span", { className: "text-sm font-semibold opacity-70", style: { color: theme.text } },
- item.year
- )
- ),
- React.createElement("h4", { className: "font-bold text-xl truncate w-full", style: { color: theme.text }, title: item.title }, item.title),
- item.subtitle && (
- React.createElement("p", { className: "text-base opacity-80 break-words", style: { color: theme.text } }, item.subtitle)
- )
- )
- )
- );
- } else {
- return (
- React.createElement("div", { key: item.id, className: "flex items-center gap-4 p-4 rounded-lg shadow-sm", style: { backgroundColor: 'rgba(128,128,128,0.1)' } },
- imageUrl && (
- React.createElement("div", { className: "p-0.5 rounded-full bg-gradient-to-br from-yellow-300 via-amber-400 to-orange-500 flex-shrink-0" },
- React.createElement("img", { src: imageUrl, alt: item.title, className: "w-16 h-16 rounded-full object-cover border-2 border-transparent", style: { borderColor: theme.bg } })
- )
- ),
- React.createElement("div", { className: "flex-1 min-w-0" },
- React.createElement("div", { className: "flex justify-between items-start" },
- React.createElement("h4", { className: "font-bold text-lg", style: { color: theme.text } }, item.title),
- item.year && (
- React.createElement("span", { className: "text-xs font-semibold px-2 py-0.5 rounded-full flex-shrink-0 ml-2", style: { backgroundColor: 'rgba(128,128,128,0.1)', color: theme.text } },
- item.year
- )
- )
- ),
- item.subtitle && (
- React.createElement("p", { className: "text-sm opacity-80", style: { color: theme.text } }, item.subtitle)
- )
- )
- )
- );
- }
- })
- )
- );
- }
- case 'form': {
- const formBlock = block as FormBlock;
- return <FormBlockComponent block={formBlock} theme={theme} onFormSubmit={onFormSubmit} />;
- }
- case 'footer': {
- const footerBlock = block as FooterBlock;
- const footerTextColor = isMobile ? theme.text : (design.sideNavSettings.textColor || theme.text);
-
- if (footerBlock.layout === 'centered') {
- return React.createElement("footer", { className: "mt-8 py-8 px-4 text-center space-y-4", style: { backgroundColor: isMobile ? 'rgba(128,128,128,0.1)' : design.sideNavSettings.backgroundColor, color: footerTextColor } },
- (footerBlock.navLinks.length > 0 || footerBlock.otherLinks.length > 0) && React.createElement("div", { className: "flex justify-center flex-wrap gap-x-6 gap-y-2" },
- [...footerBlock.navLinks, ...footerBlock.otherLinks].map(link =>
- React.createElement("a", { key: link.id, href: link.url, className: "text-sm hover:underline", style: { color: footerTextColor } }, link.title)
- )
- ),
- footerBlock.statement && React.createElement("p", { className: "text-xs max-w-2xl mx-auto", style: { opacity: 0.8 } }, footerBlock.statement),
- React.createElement("div", { className: "text-xs", style: { opacity: 0.7 } },
- React.createElement("p", null, footerBlock.copyrightText),
- footerBlock.legalText && React.createElement("p", null, footerBlock.legalText)
- )
- );
- }
- // Standard Layout
- return React.createElement("footer", { className: "mt-8 py-10 px-6", style: { backgroundColor: isMobile ? 'rgba(128,128,128,0.1)' : design.sideNavSettings.backgroundColor, color: footerTextColor } },
- React.createElement("div", { className: "grid grid-cols-1 md:grid-cols-4 gap-8" },
- React.createElement("div", { className: "md:col-span-2" },
- footerBlock.statement && React.createElement("p", { className: "text-sm", style: { opacity: 0.8 } }, footerBlock.statement)
- ),
- React.createElement("div", null,
- React.createElement("h4", { className: "font-semibold mb-3", style: { color: footerTextColor } }, "Navigation"),
- React.createElement("ul", { className: "space-y-2" },
- footerBlock.navLinks.map(link => React.createElement("li", { key: link.id }, React.createElement("a", { href: link.url, className: "text-sm hover:underline", style: { color: footerTextColor, opacity: 0.9 } }, link.title)))
- )
- ),
- React.createElement("div", null,
- React.createElement("h4", { className: "font-semibold mb-3", style: { color: footerTextColor } }, "Links"),
- React.createElement("ul", { className: "space-y-2" },
- footerBlock.otherLinks.map(link => React.createElement("li", { key: link.id }, React.createElement("a", { href: link.url, className: "text-sm hover:underline", style: { color: footerTextColor, opacity: 0.9 } }, link.title)))
- )
- )
- ),
- React.createElement("div", { className: "mt-8 pt-6 border-t", style: { borderColor: isMobile ? 'rgba(128,128,128,0.2)' : design.sideNavSettings.hoverBackgroundColor, color: footerTextColor, opacity: 0.7 } },
- React.createElement("div", { className: "flex flex-col md:flex-row justify-between text-xs" },
- React.createElement("p", null, footerBlock.copyrightText),
- footerBlock.legalText && React.createElement("p", null, footerBlock.legalText)
- )
- )
- );
- }
- default:
- return null;
- }
- };
-
- if (previewMode === 'personal') {
- const avatarUrl = getImageUrl(design.avatarSource);
-
- if (deviceView === 'mobile') {
- const chatButtonStyle: React.CSSProperties = {
- animationDuration: '3s',
- };
- if (design.buttonStyle === 'filled') {
- chatButtonStyle.backgroundColor = '#10b981';
- chatButtonStyle.color = '#ffffff';
- } else {
- chatButtonStyle.backgroundColor = 'transparent';
- chatButtonStyle.color = '#10b981';
- chatButtonStyle.borderColor = '#10b981';
- }
- return React.createElement("div", { className: "absolute inset-0 flex items-center justify-center p-4 bg-gray-200 dark:bg-gray-700" },
- React.createElement("div", {
- key: "personal-preview-screen",
- ref: scrollContainerRef,
- className: "relative w-[375px] h-[812px] max-h-full max-w-full overflow-y-auto bg-white dark:bg-gray-900 rounded-2xl shadow-xl " + getFontClass(design.fontFamily) + " " + design.fontSize + " " + getBackgroundClass(design.backgroundType, design.backgroundValue),
- style: getBackgroundStyle(design.backgroundType, design.backgroundValue)
- },
- React.createElement("div", { className: "w-full p-4 text-center flex flex-col min-h-full" },
- React.createElement("div", { className: "flex-grow" },
- avatarUrl && React.createElement("img", { src: avatarUrl, alt: "Avatar", className: "w-32 h-32 rounded-full mx-auto mt-8 mb-6 border-4 border-white dark:border-gray-700 shadow-lg" }),
- chatBlock && chatBlock.layout === 'under_avatar' && React.createElement("button", {
- onClick: () => setIsChatOpen(true),
- className: getButtonClasses(design.buttonShape, design.buttonStyle).replace('w-full p-3', 'w-auto p-3 px-6') + " mb-8 animate-breathing mx-auto",
- style: chatButtonStyle
- } as any, "Chat with me"),
- React.createElement("div", { className: "space-y-4" },
- visibleBlocks.filter(b => b.type !== 'footer').map(function(block) {
- return React.createElement("div", { key: block.id }, renderBlock(block));
- })
- )
- ),
- React.createElement("div", { className: "flex-shrink-0 -mx-4 -mb-4" },
- visibleBlocks.filter(b => b.type === 'footer').map(block => React.createElement("div", { key: block.id }, renderBlock(block)))
- )
- ),
- chatBlock && chatBlock.layout === 'widget' && React.createElement(ChatWidget, { settings: design.chatWidgetSettings }),
- chatBlock && chatBlock.layout !== 'widget' && React.createElement(PersonalChatOverlay, { isOpen: isChatOpen, onClose: function() { return setIsChatOpen(false); }, settings: design.chatWidgetSettings }),
- React.createElement(ActionModal, { data: actionModalData, onClose: function() { return setActionModalData(null); } })
- )
- );
- }
- // PC View for Personal
- const chatButtonStyle: React.CSSProperties = {
- animationDuration: '3s',
- };
- if (design.buttonStyle === 'filled') {
- chatButtonStyle.backgroundColor = '#10b981';
- chatButtonStyle.color = '#ffffff';
- } else {
- chatButtonStyle.backgroundColor = 'transparent';
- chatButtonStyle.color = '#10b981';
- chatButtonStyle.borderColor = '#10b981';
- }
- return React.createElement("div", {
- key: "personal-preview-pc",
- ref: scrollContainerRef,
- className: "absolute inset-0 overflow-y-auto " + getFontClass(design.fontFamily) + " " + design.fontSize + " " + getBackgroundClass(design.backgroundType, design.backgroundValue),
- style: Object.assign({}, getBackgroundStyle(design.backgroundType, design.backgroundValue))
- },
- React.createElement("div", { className: "w-full max-w-md mx-auto py-8 px-4" },
- React.createElement("div", null,
- avatarUrl && React.createElement("img", { src: avatarUrl, alt: "Avatar", className: "w-32 h-32 rounded-full mx-auto mb-6 border-4 border-white dark:border-gray-700 shadow-lg" })
- ),
- chatBlock && chatBlock.layout === 'under_avatar' && React.createElement("button", {
- onClick: () => setIsChatOpen(true),
- className: getButtonClasses(design.buttonShape, design.buttonStyle).replace('w-full p-3', 'w-auto p-3 px-6') + " mb-8 animate-breathing mx-auto",
- style: chatButtonStyle
- } as any, "Chat with me"),
- React.createElement("div", { className: "space-y-4" },
- visibleBlocks.filter(b => b.type !== 'footer').map(function(block) {
- return React.createElement("div", { key: block.id }, renderBlock(block));
- })
- )
- ),
- visibleBlocks.filter(b => b.type === 'footer').map(block => React.createElement("div", { key: block.id }, renderBlock(block))),
- chatBlock && chatBlock.layout === 'widget' && React.createElement(ChatWidget, { settings: design.chatWidgetSettings }),
- chatBlock && chatBlock.layout !== 'widget' && React.createElement(PersonalChatOverlay, { isOpen: isChatOpen, onClose: function() { return setIsChatOpen(false); }, settings: design.chatWidgetSettings }),
- React.createElement(ActionModal, { data: actionModalData, onClose: function() { return setActionModalData(null); } })
- );
- }
-
- // Enterprise Mode
- if (deviceView === 'mobile') {
- return React.createElement("div", { className: "absolute inset-0 flex items-center justify-center p-4 bg-gray-200 dark:bg-gray-700" },
- React.createElement("div", {
- key: "enterprise-preview-screen",
- className: "relative w-[375px] h-[812px] max-h-full max-w-full bg-white dark:bg-gray-900 rounded-2xl shadow-xl overflow-hidden " + getFontClass(design.fontFamily) + " " + design.fontSize + " " + getBackgroundClass(design.backgroundType, design.backgroundValue),
- style: Object.assign({}, getBackgroundStyle(design.backgroundType, design.backgroundValue))
- },
- React.createElement(EnterpriseNav, { blocks: visibleBlocks, deviceView: deviceView, scrollContainerRef: scrollContainerRef, settings: design.sideNavSettings }),
- React.createElement("div", {
- ref: scrollContainerRef,
- className: "h-full w-full overflow-y-auto scroll-smooth"
- },
- React.createElement("div", { className: "flex flex-col min-h-full" },
- React.createElement("div", { className: "p-4 flex-grow" },
- design.bannerSettings.type !== 'none' && (
- React.createElement("div", { className: "mb-8 rounded-lg overflow-hidden " + getBannerClass(design.bannerSettings), style: getBannerStyle(design.bannerSettings) })
- ),
- React.createElement("div", { className: "space-y-4" },
- visibleBlocks.filter(b => b.type !== 'footer').map(function(block) {
- return React.createElement("div", { key: block.id, id: 'block-' + block.id }, renderBlock(block));
- })
- )
- ),
- React.createElement("div", { className: "flex-shrink-0" },
- visibleBlocks.filter(b => b.type === 'footer').map(block => React.createElement("div", { key: block.id, id: 'block-' + block.id }, renderBlock(block)))
- )
- )
- ),
- chatBlock && chatBlock.layout === 'widget' && React.createElement(ChatWidget, { settings: design.chatWidgetSettings }),
- React.createElement(ActionModal, { data: actionModalData, onClose: function() { return setActionModalData(null); } })
- )
- );
- }
- // PC View for Enterprise
- const navFloatStyle = design.sideNavSettings.navFloatStyle || 'normal';
- const navBackgroundStyle = design.sideNavSettings.navBackgroundStyle || 'compact';
- const navContent = React.createElement(EnterpriseNav, { blocks: visibleBlocks, deviceView: 'pc', scrollContainerRef: scrollContainerRef, settings: design.sideNavSettings });
- let navColumn;
- if (navBackgroundStyle === 'full') {
- const columnClasses = "w-64 flex-shrink-0";
- const columnStyle = { backgroundColor: design.sideNavSettings.backgroundColor };
- let contentWrapperClasses = 'w-full';
- if (navFloatStyle === 'normal') contentWrapperClasses += ' pt-8';
- else if (navFloatStyle === 'top') contentWrapperClasses += ' sticky top-8';
- else if (navFloatStyle === 'center') contentWrapperClasses += ' sticky top-0 h-screen flex flex-col justify-center';
- navColumn = React.createElement("div", { className: columnClasses, style: columnStyle }, React.createElement("div", { className: contentWrapperClasses }, navContent));
- } else {
- let columnClasses = 'w-64 flex-shrink-0';
- if (navFloatStyle === 'normal') columnClasses += ' pt-8';
- else if (navFloatStyle === 'top') columnClasses += ' sticky top-8 self-start';
- else if (navFloatStyle === 'center') columnClasses += ' sticky top-0 h-screen flex flex-col justify-center';
- navColumn = React.createElement("div", { className: columnClasses }, navContent);
- }
- return React.createElement("div", {
- key: "enterprise-preview",
- className: "h-full relative " + getFontClass(design.fontFamily) + " " + design.fontSize + " " + getBackgroundClass(design.backgroundType, design.backgroundValue),
- style: Object.assign({}, getBackgroundStyle(design.backgroundType, design.backgroundValue))
- },
- React.createElement("div", {
- ref: scrollContainerRef,
- className: "h-full w-full overflow-y-auto scroll-smooth"
- },
- React.createElement("div", {
- className: "flex flex-col min-h-full"
- },
- React.createElement("div", {
- className: "mx-auto flex items-stretch gap-8 px-8 flex-grow",
- style: { maxWidth: '1344px' }
- },
- navColumn,
- React.createElement("main", {
- className: "flex-1 min-w-0 py-8"
- },
- design.bannerSettings.type !== 'none' && (
- React.createElement("div", { className: "mb-8 rounded-lg overflow-hidden " + (design.bannerSettings.width === 'contained' ? '' : '-mx-8') + " " + getBannerClass(design.bannerSettings), style: getBannerStyle(design.bannerSettings) })
- ),
- React.createElement("div", { className: "space-y-4" },
- visibleBlocks.filter(b => b.type !== 'footer').map(function(block) {
- return React.createElement("div", { key: block.id, id: 'block-' + block.id }, renderBlock(block));
- })
- )
- )
- ),
- React.createElement("div", { className: "flex-shrink-0" },
- visibleBlocks.filter(b => b.type === 'footer').map(block => React.createElement("div", { key: block.id, id: 'block-' + block.id }, renderBlock(block)))
- )
- )
- ),
- chatBlock && chatBlock.layout === 'widget' && React.createElement(ChatWidget, { settings: design.chatWidgetSettings }),
- React.createElement(ActionModal, { data: actionModalData, onClose: function() { return setActionModalData(null); } })
- );
- };
|