"use client"; // One System content for React — v1 (https://copilot.onesystemcrm.com/developers) // // Copy this file into your project as onesystem/react.tsx (next to onesystem/content.ts). // Wrap the app once in , then read items with useContent("home.hero.title"). // Always show content as text ({title}), never with dangerouslySetInnerHTML. import { createContext, useContext, useEffect, useState, type ReactNode } from "react"; import { loadContent, previewToken, type Content, type ContentFile } from "./content"; const ContentContext = createContext({}); type Props = { /** The Site ID from One System */ site?: string; /** public/onesystem-content.json, imported */ file: ContentFile; /** Next.js: the content loaded on the server (no waiting in the browser) */ initial?: Content; /** Vite/React: how long to wait for One System before showing the built-in copy (default 1500 ms) */ waitMs?: number; children: ReactNode; }; /** * Vite / React: * Next.js: pass `initial` from a server component (see the README). Preview links (?os_preview=…) work in both. */ export function OneSystemContent({ site, file, initial, waitMs, children }: Props) { const [values, setValues] = useState(initial ?? null); useEffect(() => { const preview = previewToken(); if (initial && !preview) return; let live = true; loadContent({ site, file, preview, timeoutMs: waitMs }).then((v) => { if (live) setValues(v); }); return () => { live = false; }; }, [site, file, initial, waitMs]); // Vite: wait a moment (at most waitMs) instead of flashing the built-in text and then the owner's newer text. if (!values) return null; return {children}; } /** One item's current value, e.g. useContent("contact.phone"). "" when the item doesn't exist. */ export function useContent(key: string): string { return useContext(ContentContext)[key] ?? ""; } /** Every item (for lists you build from several keys). */ export function useAllContent(): Content { return useContext(ContentContext); } /** * Vite / React apps: sets the page's Google title and description from the items "seo..title" and * "seo..description" (e.g. "seo./about.title"). Next.js: use generateMetadata instead (see the README). */ export function useSeo(path: string) { const values = useContext(ContentContext); useEffect(() => { const title = values[`seo.${path}.title`]; const description = values[`seo.${path}.description`]; if (title) document.title = title; if (description) { let tag = document.querySelector('meta[name="description"]'); if (!tag) { tag = document.createElement("meta"); tag.setAttribute("name", "description"); document.head.appendChild(tag); } tag.setAttribute("content", description); } }, [values, path]); }