From f6243d3ffe5849e6e57725adca3fe2fd9e0e4f29 Mon Sep 17 00:00:00 2001 From: Josh Knapp Date: Sat, 23 May 2026 14:16:20 -0700 Subject: [PATCH] sitesmith: useSitesmith hook (entitlement, history, send) --- craft/src/hooks/useSitesmith.ts | 53 +++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 craft/src/hooks/useSitesmith.ts diff --git a/craft/src/hooks/useSitesmith.ts b/craft/src/hooks/useSitesmith.ts new file mode 100644 index 0000000..87dae41 --- /dev/null +++ b/craft/src/hooks/useSitesmith.ts @@ -0,0 +1,53 @@ +import { useCallback, useEffect, useState } from 'react'; +import { useEditorConfig } from '../state/EditorConfigContext'; +import { SitesmithSummary, SitesmithMessage, SendResult } from '../types/sitesmith'; + +function apiBase(apiUrl: string): string { + return apiUrl.replace(/site-builder\.php$/, 'sitesmith.php'); +} + +export function useSitesmith(siteId: number) { + const { whpConfig } = useEditorConfig(); + const [summary, setSummary] = useState(null); + const [messages, setMessages] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + const refreshEntitlement = useCallback(async () => { + if (!whpConfig) return; + try { + const r = await fetch(`${apiBase(whpConfig.apiUrl)}?action=entitlement`, { credentials: 'include' }); + const j = await r.json(); + if (j.ok) setSummary(j.summary); + } catch (e: any) { setError(String(e?.message ?? e)); } + }, [whpConfig]); + + const fetchHistory = useCallback(async () => { + if (!whpConfig) { setLoading(false); return; } + try { + const r = await fetch(`${apiBase(whpConfig.apiUrl)}?action=history&site_id=${siteId}`, { credentials: 'include' }); + const j = await r.json(); + if (j.ok) setMessages(j.messages); + } catch (e: any) { setError(String(e?.message ?? e)); } + finally { setLoading(false); } + }, [whpConfig, siteId]); + + useEffect(() => { void refreshEntitlement(); void fetchHistory(); }, [refreshEntitlement, fetchHistory]); + + const send = useCallback(async (userText: string, canvasSummary: string): Promise => { + if (!whpConfig) return { ok: false, status: 'BLOCKED', message: 'No WHP config' }; + setMessages((m) => [...m, { role: 'user', content: userText, response_type: null, created_at: new Date().toISOString() }]); + const r = await fetch(`${apiBase(whpConfig.apiUrl)}?action=send`, { + method: 'POST', + credentials: 'include', + headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': whpConfig.csrfToken }, + body: JSON.stringify({ site_id: siteId, message: userText, canvas_summary: canvasSummary }), + }); + const j: SendResult = await r.json(); + void fetchHistory(); + void refreshEntitlement(); + return j; + }, [whpConfig, siteId, fetchHistory, refreshEntitlement]); + + return { summary, messages, loading, error, send, refreshEntitlement }; +}