/* global React */ const { useState, useEffect, useRef } = window.ReactHooks; // Reusable primitives: links, nav, footer, bot, tweaks panel function useRoute() { const get = () => { const h = window.location.hash.replace(/^#\/?/, ""); const [path, query] = h.split("?"); return { path: path || "home", query: query || "" }; }; const [route, setRoute] = useState(get()); useEffect(() => { const on = () => setRoute(get()); window.addEventListener("hashchange", on); return () => window.removeEventListener("hashchange", on); }, []); return route; } function go(path) { window.location.hash = "#/" + path; } function Link({ to, className, children, onClick, ...rest }) { return ( { onClick && onClick(e); }} {...rest} > {children} ); } function Nav({ route }) { const is = (p) => (route.path === p || (p === "catalog" && route.path.startsWith("catalog")) || (p === "product" && route.path.startsWith("product")) ? "active" : ""); return (
TC tcviz
Contact sales Browse visuals
); } function Footer() { return ( ); } function SupportBot() { const [open, setOpen] = useState(false); const [messages, setMessages] = useState([ { who: "bot", text: "Hi — I'm the tcviz assistant. This is a preview; a full support bot will go live soon. How can I help?" } ]); const [input, setInput] = useState(""); const scrollRef = useRef(null); useEffect(() => { if (scrollRef.current) scrollRef.current.scrollTop = scrollRef.current.scrollHeight; }, [messages, open]); const quick = [ "Installation on Power BI Desktop", "Licensing & seats", "Compatibility with Fabric", "Request a demo" ]; const send = (text) => { if (!text || !text.trim()) return; const me = { who: "me", text: text.trim() }; setMessages((m) => [...m, me]); setInput(""); setTimeout(() => { setMessages((m) => [ ...m, { who: "bot", text: "Thanks — a human from TC will pick this up. In the meantime you can also reach us at support@tcviz.com." } ]); }, 600); }; return (
{open && (
TC Support
Preview · replies in under 4h
{messages.map((m, i) => (
{m.text}
))}
{quick.map((q) => ( ))}
setInput(e.target.value)} onKeyDown={(e) => e.key === "Enter" && send(input)} /> ENTER
)}
); } function TweaksPanel({ tweaks, setTweak }) { const accents = [ ["#2B5CFF", "Cobalt"], ["#0A0A0A", "Ink"], ["#D95E2A", "Persimmon"], ["#1E7A3A", "Pine"], ["#7C3AED", "Violet"] ]; return (
Tweaks
{accents.map(([c, name]) => (
); } Object.assign(window, { useRoute, go, Link, Nav, Footer, SupportBot, TweaksPanel });