import { useCallback } from 'react'; import { useEditor } from '@craftjs/core'; import { useEditorConfig } from '../state/EditorConfigContext'; import { usePages } from '../state/PageContext'; import { exportBodyHtml } from '../utils/html-export'; export function useWhpApi() { const { query, actions } = useEditor(); const { whpConfig, isWHP } = useEditorConfig(); const { pages, headerPage, footerPage, activePageId, setHeaderCraftState, setFooterCraftState, setPagesCraftState } = usePages(); const save = useCallback(async () => { if (!isWHP || !whpConfig) return null; // Serialize the current canvas state (whatever page is active) const currentCraftState = query.serialize(); // Export body HTML for the current page let currentHtml = ''; let css = ''; try { const result = exportBodyHtml(currentCraftState); currentHtml = result.html; css = result.css; } catch (e) { console.error('HTML export failed, saving state only:', e); } // Export header HTML from its craft state let headerHtml = ''; try { if (headerPage.craftState) { const hResult = exportBodyHtml(headerPage.craftState); headerHtml = hResult.html; } } catch (e) { console.error('Header HTML export failed:', e); } // Export footer HTML from its craft state let footerHtml = ''; try { if (footerPage.craftState) { const fResult = exportBodyHtml(footerPage.craftState); footerHtml = fResult.html; } } catch (e) { console.error('Footer HTML export failed:', e); } // Build the pages array with HTML for each page // For the active page, use the freshly exported HTML from the canvas; // for others, export from their stored craft state const pagesPayload = pages.map((page) => { const filename = (page.slug === 'index' ? 'index' : page.slug) + '.html'; let pageHtml = ''; if (page.id === activePageId) { // Active page: use the current canvas HTML (already exported above) pageHtml = currentHtml; } else if (page.craftState) { try { const pResult = exportBodyHtml(page.craftState); pageHtml = pResult.html; } catch (e) { console.error(`HTML export failed for page ${page.name}:`, e); } } return { filename, title: page.name, html: pageHtml, }; }); // Build pages_craft_state array: for each page, store its craft state // For the currently active page, always use the fresh canvas state (currentCraftState) // since page.craftState may be stale (not updated until page switch) const pagesGrapesjs = pages.map((page) => ({ id: page.id, name: page.name, slug: page.slug, craftState: page.id === activePageId ? currentCraftState : (page.craftState || null), })); const payload = { site_id: whpConfig.siteId, name: whpConfig.siteName, html: currentHtml, css, pages: pagesPayload, header_html: headerHtml, footer_html: footerHtml, craft_state: currentCraftState, header_craft_state: headerPage.craftState || null, footer_craft_state: footerPage.craftState || null, pages_craft_state: pagesGrapesjs, }; const resp = await fetch(`${whpConfig.apiUrl}?action=save`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': whpConfig.csrfToken, }, body: JSON.stringify(payload), }); return resp.json(); }, [isWHP, whpConfig, query, pages, activePageId, headerPage, footerPage]); const publish = useCallback(async () => { if (!isWHP || !whpConfig) return null; // First save to ensure staging is up to date await save(); // Then publish from staging to live const resp = await fetch( `${whpConfig.apiUrl}?action=publish&site_id=${whpConfig.siteId}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': whpConfig.csrfToken, }, body: JSON.stringify({ site_id: whpConfig.siteId }), }, ); return resp.json(); }, [isWHP, whpConfig, save]); const load = useCallback(async () => { if (!isWHP || !whpConfig) return null; const resp = await fetch( `${whpConfig.apiUrl}?action=load&site_id=${whpConfig.siteId}`, ); const data = await resp.json(); if (data.success && data.project) { const proj = data.project; // Restore header craft state if (proj.header_craft_state) { setHeaderCraftState(typeof proj.header_craft_state === 'string' ? proj.header_craft_state : JSON.stringify(proj.header_craft_state)); } // Restore footer craft state if (proj.footer_craft_state) { setFooterCraftState(typeof proj.footer_craft_state === 'string' ? proj.footer_craft_state : JSON.stringify(proj.footer_craft_state)); } // Restore pages and load the first page into the canvas if (proj.pages_craft_state && Array.isArray(proj.pages_craft_state) && proj.pages_craft_state.length > 0) { setPagesCraftState(proj.pages_craft_state.map((p: { id: string; name: string; slug: string; craftState: string | null }) => ({ id: p.id, name: p.name, slug: p.slug, craftState: p.craftState || null, }))); // Load the first page (home) into the canvas const firstPage = proj.pages_craft_state[0]; if (firstPage.craftState) { try { const state = typeof firstPage.craftState === 'string' ? firstPage.craftState : JSON.stringify(firstPage.craftState); actions.deserialize(state); } catch (e) { console.warn('Failed to load page state:', e); } } } } return data; }, [isWHP, whpConfig, actions, setHeaderCraftState, setFooterCraftState, setPagesCraftState]); const uploadAsset = useCallback( async (file: File) => { if (!isWHP || !whpConfig) return null; const formData = new FormData(); formData.append('file', file); const resp = await fetch( `${whpConfig.apiUrl}?action=upload_asset&site_id=${whpConfig.siteId}`, { method: 'POST', headers: { 'X-CSRF-Token': whpConfig.csrfToken }, body: formData, }, ); return resp.json(); }, [isWHP, whpConfig], ); return { save, publish, load, uploadAsset, isWHP }; }