import { useState, useEffect, useRef, useCallback, useMemo } from "react"; // ═══════════════════════════════════════════════════════════════════ // TRUSTGRAPH RETAIL INTELLIGENCE DEMO // Ontology-Driven Context Graph: Consumer × Agent × Retail × Brand // ═══════════════════════════════════════════════════════════════════ // ── Ontology Schema ────────────────────────────────────────────── const ONTOLOGY = { consumer: { label: "Consumer", color: "#6EE7B7", glow: "rgba(110,231,183,0.4)", icon: "👤", description: "Individuals and segments interacting with brands through retail channels", properties: ["segment", "preferences", "journeyStage", "lifetime_value", "sentiment"], subclasses: [ { id: "cs1", label: "Urban Millennials", props: { size: "2.4M", avgSpend: "$142/mo", loyalty: 0.78, journeyStage: "Engaged" } }, { id: "cs2", label: "Active Families", props: { size: "1.8M", avgSpend: "$218/mo", loyalty: 0.85, journeyStage: "Loyal" } }, { id: "cs3", label: "Eco-Conscious Gen Z", props: { size: "3.1M", avgSpend: "$96/mo", loyalty: 0.62, journeyStage: "Exploring" } }, { id: "cs4", label: "Luxury Seekers", props: { size: "890K", avgSpend: "$384/mo", loyalty: 0.91, journeyStage: "Advocate" } }, { id: "cs5", label: "Weekend Warriors", props: { size: "1.5M", avgSpend: "$167/mo", loyalty: 0.73, journeyStage: "Engaged" } }, ], }, brand: { label: "Brand", color: "#F9A8D4", glow: "rgba(249,168,212,0.4)", icon: "✦", description: "Product brands seeking to connect with consumers through retail experiences", properties: ["identity", "positioning", "campaigns", "products", "partnerships"], subclasses: [ { id: "br1", label: "Lumière Beauty", props: { category: "Cosmetics", positioning: "Premium", campaigns: 12, sentiment: 0.87 } }, { id: "br2", label: "Nordic Trail", props: { category: "Outdoor Apparel", positioning: "Sustainable", campaigns: 8, sentiment: 0.82 } }, { id: "br3", label: "Velo Sport", props: { category: "Athletics", positioning: "Performance", campaigns: 15, sentiment: 0.79 } }, { id: "br4", label: "Casa Verde", props: { category: "Home & Living", positioning: "Artisanal", campaigns: 6, sentiment: 0.90 } }, { id: "br5", label: "Artisan Coffee Co.", props: { category: "F&B", positioning: "Community", campaigns: 10, sentiment: 0.85 } }, ], }, retail: { label: "Retail", color: "#93C5FD", glow: "rgba(147,197,253,0.4)", icon: "🏬", description: "Channels, touchpoints, and experiences where brands meet consumers", properties: ["channel", "location", "traffic", "conversionRate", "experience_score"], subclasses: [ { id: "rt1", label: "Flagship Store NYC", props: { channel: "Physical", traffic: "48K/mo", conversion: "12.3%", experience: 0.91 } }, { id: "rt2", label: "Mobile Commerce App", props: { channel: "Digital", traffic: "1.2M/mo", conversion: "4.7%", experience: 0.78 } }, { id: "rt3", label: "Pop-Up Experience", props: { channel: "Experiential", traffic: "8K/event", conversion: "18.6%", experience: 0.95 } }, { id: "rt4", label: "Social Commerce", props: { channel: "Social", traffic: "890K/mo", conversion: "3.2%", experience: 0.72 } }, { id: "rt5", label: "Loyalty Hub", props: { channel: "Omnichannel", traffic: "340K/mo", conversion: "22.1%", experience: 0.88 } }, ], }, agent: { label: "Agent", color: "#FCD34D", glow: "rgba(252,211,77,0.4)", icon: "⚡", description: "AI agents that orchestrate personalized brand-consumer connections", properties: ["capability", "contextSources", "accuracy", "latency", "decisions_per_day"], subclasses: [ { id: "ag1", label: "Recommendation Agent", props: { capability: "Product Discovery", accuracy: "94.2%", latency: "120ms", decisions: "2.1M/day" } }, { id: "ag2", label: "Personalization Agent", props: { capability: "Experience Tailoring", accuracy: "91.8%", latency: "85ms", decisions: "890K/day" } }, { id: "ag3", label: "Campaign Orchestrator", props: { capability: "Brand Activation", accuracy: "88.5%", latency: "200ms", decisions: "340K/day" } }, { id: "ag4", label: "Sentiment Analyst", props: { capability: "Brand Perception", accuracy: "96.1%", latency: "150ms", decisions: "1.5M/day" } }, { id: "ag5", label: "Journey Optimizer", props: { capability: "Path Optimization", accuracy: "89.7%", latency: "180ms", decisions: "560K/day" } }, ], }, }; // ── Ontology Relationships (Triples) ────────────────────────────── const RELATIONSHIPS = [ // Consumer ↔ Brand { from: "cs1", to: "br1", predicate: "has_affinity_for", strength: 0.85, domain: ["consumer", "brand"] }, { from: "cs1", to: "br5", predicate: "frequents", strength: 0.69, domain: ["consumer", "brand"] }, { from: "cs2", to: "br2", predicate: "has_affinity_for", strength: 0.78, domain: ["consumer", "brand"] }, { from: "cs2", to: "br3", predicate: "purchases_from", strength: 0.88, domain: ["consumer", "brand"] }, { from: "cs3", to: "br2", predicate: "advocates_for", strength: 0.71, domain: ["consumer", "brand"] }, { from: "cs3", to: "br4", predicate: "has_affinity_for", strength: 0.65, domain: ["consumer", "brand"] }, { from: "cs3", to: "br5", predicate: "frequents", strength: 0.58, domain: ["consumer", "brand"] }, { from: "cs4", to: "br1", predicate: "loyal_to", strength: 0.92, domain: ["consumer", "brand"] }, { from: "cs4", to: "br4", predicate: "purchases_from", strength: 0.82, domain: ["consumer", "brand"] }, { from: "cs5", to: "br3", predicate: "has_affinity_for", strength: 0.76, domain: ["consumer", "brand"] }, { from: "cs5", to: "br5", predicate: "frequents", strength: 0.74, domain: ["consumer", "brand"] }, // Consumer ↔ Retail { from: "cs1", to: "rt2", predicate: "shops_via", strength: 0.82, domain: ["consumer", "retail"] }, { from: "cs1", to: "rt4", predicate: "discovers_through", strength: 0.71, domain: ["consumer", "retail"] }, { from: "cs2", to: "rt1", predicate: "shops_via", strength: 0.85, domain: ["consumer", "retail"] }, { from: "cs2", to: "rt5", predicate: "member_of", strength: 0.90, domain: ["consumer", "retail"] }, { from: "cs3", to: "rt3", predicate: "experiences", strength: 0.88, domain: ["consumer", "retail"] }, { from: "cs3", to: "rt4", predicate: "discovers_through", strength: 0.79, domain: ["consumer", "retail"] }, { from: "cs4", to: "rt1", predicate: "shops_via", strength: 0.94, domain: ["consumer", "retail"] }, { from: "cs4", to: "rt3", predicate: "experiences", strength: 0.86, domain: ["consumer", "retail"] }, { from: "cs5", to: "rt2", predicate: "shops_via", strength: 0.72, domain: ["consumer", "retail"] }, { from: "cs5", to: "rt5", predicate: "member_of", strength: 0.68, domain: ["consumer", "retail"] }, // Brand ↔ Retail { from: "br1", to: "rt1", predicate: "merchandises_in", strength: 0.90, domain: ["brand", "retail"] }, { from: "br1", to: "rt3", predicate: "activates_via", strength: 0.85, domain: ["brand", "retail"] }, { from: "br2", to: "rt3", predicate: "activates_via", strength: 0.82, domain: ["brand", "retail"] }, { from: "br2", to: "rt4", predicate: "promotes_on", strength: 0.75, domain: ["brand", "retail"] }, { from: "br3", to: "rt2", predicate: "sells_through", strength: 0.80, domain: ["brand", "retail"] }, { from: "br3", to: "rt5", predicate: "rewards_via", strength: 0.77, domain: ["brand", "retail"] }, { from: "br4", to: "rt1", predicate: "merchandises_in", strength: 0.88, domain: ["brand", "retail"] }, { from: "br4", to: "rt4", predicate: "promotes_on", strength: 0.70, domain: ["brand", "retail"] }, { from: "br5", to: "rt3", predicate: "activates_via", strength: 0.79, domain: ["brand", "retail"] }, { from: "br5", to: "rt5", predicate: "rewards_via", strength: 0.83, domain: ["brand", "retail"] }, // Agent ↔ Consumer { from: "ag1", to: "cs1", predicate: "recommends_to", strength: 0.87, domain: ["agent", "consumer"] }, { from: "ag1", to: "cs3", predicate: "recommends_to", strength: 0.81, domain: ["agent", "consumer"] }, { from: "ag2", to: "cs4", predicate: "personalizes_for", strength: 0.93, domain: ["agent", "consumer"] }, { from: "ag2", to: "cs2", predicate: "personalizes_for", strength: 0.84, domain: ["agent", "consumer"] }, { from: "ag4", to: "cs1", predicate: "monitors_sentiment_of", strength: 0.78, domain: ["agent", "consumer"] }, { from: "ag4", to: "cs3", predicate: "monitors_sentiment_of", strength: 0.82, domain: ["agent", "consumer"] }, { from: "ag5", to: "cs2", predicate: "optimizes_journey_for", strength: 0.86, domain: ["agent", "consumer"] }, { from: "ag5", to: "cs5", predicate: "optimizes_journey_for", strength: 0.75, domain: ["agent", "consumer"] }, // Agent ↔ Brand { from: "ag3", to: "br1", predicate: "orchestrates_campaign_for", strength: 0.88, domain: ["agent", "brand"] }, { from: "ag3", to: "br2", predicate: "orchestrates_campaign_for", strength: 0.82, domain: ["agent", "brand"] }, { from: "ag3", to: "br5", predicate: "orchestrates_campaign_for", strength: 0.79, domain: ["agent", "brand"] }, { from: "ag4", to: "br1", predicate: "analyzes_perception_of", strength: 0.91, domain: ["agent", "brand"] }, { from: "ag4", to: "br3", predicate: "analyzes_perception_of", strength: 0.85, domain: ["agent", "brand"] }, { from: "ag1", to: "br3", predicate: "curates_products_for", strength: 0.83, domain: ["agent", "brand"] }, { from: "ag1", to: "br4", predicate: "curates_products_for", strength: 0.77, domain: ["agent", "brand"] }, // Agent ↔ Retail { from: "ag2", to: "rt1", predicate: "tailors_experience_at", strength: 0.89, domain: ["agent", "retail"] }, { from: "ag2", to: "rt2", predicate: "tailors_experience_at", strength: 0.85, domain: ["agent", "retail"] }, { from: "ag3", to: "rt3", predicate: "deploys_campaign_at", strength: 0.81, domain: ["agent", "retail"] }, { from: "ag3", to: "rt4", predicate: "deploys_campaign_at", strength: 0.86, domain: ["agent", "retail"] }, { from: "ag5", to: "rt1", predicate: "optimizes_flow_at", strength: 0.84, domain: ["agent", "retail"] }, { from: "ag5", to: "rt5", predicate: "optimizes_flow_at", strength: 0.80, domain: ["agent", "retail"] }, ]; // ── Pre-built Agent Queries & Responses ──────────────────────────── const DEMO_QUERIES = [ { q: "Which brands should activate at the Pop-Up Experience to reach Eco-Conscious Gen Z?", thinking: [ "Traversing ontology: Consumer[cs3] → has_affinity_for → Brand[br2, br4, br5]", "Traversing ontology: Consumer[cs3] → experiences → Retail[rt3]", "Cross-referencing: Brand activations at Retail[rt3]", "Ranking by: affinity strength × activation fit × conversion potential", ], answer: "Nordic Trail and Artisan Coffee Co. are the strongest activation candidates for the Pop-Up Experience targeting Eco-Conscious Gen Z. Nordic Trail's sustainability positioning aligns with this segment's values (affinity: 0.71) and already activates via experiential retail (strength: 0.82). Artisan Coffee Co. has existing frequency with this segment (0.58) and pop-up activation experience (0.79). Casa Verde is a secondary candidate — lower affinity (0.65) but high experiential fit.", entities: ["cs3", "br2", "br5", "br4", "rt3"], triples: 8, }, { q: "How should Lumière Beauty optimize its engagement with Luxury Seekers across channels?", thinking: [ "Resolving entities: Brand[br1] = Lumière Beauty, Consumer[cs4] = Luxury Seekers", "Traversing: Brand[br1] → merchandises_in → Retail[rt1], activates_via → Retail[rt3]", "Traversing: Consumer[cs4] → shops_via → Retail[rt1], experiences → Retail[rt3]", "Agent context: Personalization Agent[ag2] → personalizes_for → Consumer[cs4] (0.93)", "Aggregating channel overlap and engagement signals", ], answer: "Lumière Beauty has exceptional alignment with Luxury Seekers — the strongest brand-consumer affinity in the graph (0.92). Both converge on Flagship Store NYC and Pop-Up Experiences. The Personalization Agent already achieves 93% accuracy for this segment. Recommendation: Deepen the flagship in-store experience with agent-powered personalized consultations, and create exclusive pop-up previews. The loyalty-to-advocacy pipeline is strong (journey stage: Advocate) — leverage this for referral programs through the Loyalty Hub.", entities: ["br1", "cs4", "rt1", "rt3", "ag2", "rt5"], triples: 12, }, { q: "What's the optimal agent configuration for the Mobile Commerce channel?", thinking: [ "Resolving: Retail[rt2] = Mobile Commerce App", "Traversing: Agent → tailors_experience_at → Retail[rt2]", "Traversing: Consumer → shops_via → Retail[rt2]: [cs1, cs5]", "Traversing: Brand → sells_through → Retail[rt2]: [br3]", "Evaluating agent capabilities against mobile channel requirements", ], answer: "Mobile Commerce currently has the Personalization Agent deployed (experience tailoring, 85% strength). Primary consumer segments are Urban Millennials (0.82) and Weekend Warriors (0.72). Add the Recommendation Agent — it already serves Urban Millennials (0.87) and can curate Velo Sport products (the channel's primary brand). The Journey Optimizer should be connected to reduce the gap between the channel's high traffic (1.2M/mo) and moderate conversion (4.7%). Projected improvement: 2.1% conversion lift through graph-informed product sequencing.", entities: ["rt2", "ag2", "ag1", "ag5", "cs1", "cs5", "br3"], triples: 11, }, ]; // ── Helper: find all entities ──────────────────────────────────────── function getAllEntities() { const all = []; Object.entries(ONTOLOGY).forEach(([domain, data]) => { data.subclasses.forEach((sc) => { all.push({ ...sc, domain, color: data.color, glow: data.glow, icon: data.icon }); }); }); return all; } // ── Graph Visualization (Canvas-based force layout) ───────────────── function GraphCanvas({ highlightedEntities, onNodeClick, activeFilter }) { const canvasRef = useRef(null); const nodesRef = useRef([]); const animRef = useRef(null); const hoveredRef = useRef(null); const [hovered, setHovered] = useState(null); const entities = useMemo(() => getAllEntities(), []); useEffect(() => { const canvas = canvasRef.current; if (!canvas) return; const rect = canvas.parentElement.getBoundingClientRect(); canvas.width = rect.width * 2; canvas.height = rect.height * 2; canvas.style.width = rect.width + "px"; canvas.style.height = rect.height + "px"; const cx = canvas.width / 2; const cy = canvas.height / 2; // Position nodes in domain clusters const domainPositions = { consumer: { x: cx - cx * 0.35, y: cy - cy * 0.32 }, brand: { x: cx + cx * 0.35, y: cy - cy * 0.32 }, retail: { x: cx + cx * 0.35, y: cy + cy * 0.32 }, agent: { x: cx - cx * 0.35, y: cy + cy * 0.32 }, }; nodesRef.current = entities.map((e, i) => { const dp = domainPositions[e.domain]; const subIdx = ONTOLOGY[e.domain].subclasses.findIndex((s) => s.id === e.id); const total = ONTOLOGY[e.domain].subclasses.length; const angle = ((Math.PI * 2) / total) * subIdx - Math.PI / 2; const radius = Math.min(canvas.width, canvas.height) * 0.1; return { ...e, x: dp.x + Math.cos(angle) * radius, y: dp.y + Math.sin(angle) * radius, vx: 0, vy: 0, targetX: dp.x + Math.cos(angle) * radius, targetY: dp.y + Math.sin(angle) * radius, r: 18, }; }); const ctx = canvas.getContext("2d"); let time = 0; function draw() { time += 0.005; ctx.clearRect(0, 0, canvas.width, canvas.height); // Subtle grid ctx.strokeStyle = "rgba(255,255,255,0.015)"; ctx.lineWidth = 1; for (let x = 0; x < canvas.width; x += 60) { ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, canvas.height); ctx.stroke(); } for (let y = 0; y < canvas.height; y += 60) { ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(canvas.width, y); ctx.stroke(); } // Domain labels Object.entries(domainPositions).forEach(([domain, pos]) => { const data = ONTOLOGY[domain]; ctx.font = "bold 22px 'IBM Plex Mono', monospace"; ctx.fillStyle = data.color + "44"; ctx.textAlign = "center"; ctx.fillText(data.label.toUpperCase(), pos.x, pos.y - Math.min(canvas.width, canvas.height) * 0.14); }); // Draw edges const nodes = nodesRef.current; const filteredRels = activeFilter ? RELATIONSHIPS.filter((r) => r.domain.includes(activeFilter)) : RELATIONSHIPS; filteredRels.forEach((rel) => { const fromNode = nodes.find((n) => n.id === rel.from); const toNode = nodes.find((n) => n.id === rel.to); if (!fromNode || !toNode) return; const isHighlighted = highlightedEntities && highlightedEntities.includes(rel.from) && highlightedEntities.includes(rel.to); const baseAlpha = isHighlighted ? 0.7 : 0.12; const pulse = isHighlighted ? Math.sin(time * 4) * 0.15 + 0.15 : 0; ctx.beginPath(); ctx.moveTo(fromNode.x, fromNode.y); // Curved edges const mx = (fromNode.x + toNode.x) / 2 + (fromNode.y - toNode.y) * 0.1; const my = (fromNode.y + toNode.y) / 2 + (toNode.x - fromNode.x) * 0.1; ctx.quadraticCurveTo(mx, my, toNode.x, toNode.y); const gradient = ctx.createLinearGradient(fromNode.x, fromNode.y, toNode.x, toNode.y); gradient.addColorStop(0, fromNode.color + Math.round((baseAlpha + pulse) * 255).toString(16).padStart(2, "0")); gradient.addColorStop(1, toNode.color + Math.round((baseAlpha + pulse) * 255).toString(16).padStart(2, "0")); ctx.strokeStyle = gradient; ctx.lineWidth = isHighlighted ? 3 : 1.5; ctx.stroke(); // Animated particles on highlighted edges if (isHighlighted) { const t = (time * 2 + rel.strength) % 1; const px = (1 - t) * (1 - t) * fromNode.x + 2 * (1 - t) * t * mx + t * t * toNode.x; const py = (1 - t) * (1 - t) * fromNode.y + 2 * (1 - t) * t * my + t * t * toNode.y; ctx.beginPath(); ctx.arc(px, py, 3, 0, Math.PI * 2); ctx.fillStyle = "#fff"; ctx.fill(); } }); // Draw nodes nodes.forEach((node) => { const isHighlighted = highlightedEntities && highlightedEntities.includes(node.id); const isHovered = hoveredRef.current === node.id; const isDimmed = highlightedEntities && highlightedEntities.length > 0 && !isHighlighted; const isFiltered = activeFilter && node.domain !== activeFilter && !RELATIONSHIPS.some( r => r.domain.includes(activeFilter) && (r.from === node.id || r.to === node.id) ); const alpha = isFiltered ? 0.15 : isDimmed ? 0.3 : 1; const r = isHighlighted || isHovered ? node.r * 1.4 : node.r; const pulseR = isHighlighted ? Math.sin(time * 3) * 3 : 0; // Glow if ((isHighlighted || isHovered) && !isFiltered) { ctx.beginPath(); ctx.arc(node.x, node.y, r + 12 + pulseR, 0, Math.PI * 2); const grd = ctx.createRadialGradient(node.x, node.y, r, node.x, node.y, r + 12 + pulseR); grd.addColorStop(0, node.glow); grd.addColorStop(1, "rgba(0,0,0,0)"); ctx.fillStyle = grd; ctx.fill(); } // Node circle ctx.beginPath(); ctx.arc(node.x, node.y, r, 0, Math.PI * 2); ctx.fillStyle = node.color + Math.round(alpha * 255 * 0.2).toString(16).padStart(2, "0"); ctx.fill(); ctx.strokeStyle = node.color + Math.round(alpha * 255).toString(16).padStart(2, "0"); ctx.lineWidth = isHighlighted ? 2.5 : 1.5; ctx.stroke(); // Label ctx.font = `${isHighlighted ? "bold " : ""}${isHovered ? 17 : 14}px 'IBM Plex Sans', sans-serif`; ctx.fillStyle = `rgba(255,255,255,${alpha * (isHighlighted ? 1 : 0.75)})`; ctx.textAlign = "center"; ctx.fillText(node.label, node.x, node.y + r + 18); // Spring physics node.x += (node.targetX - node.x) * 0.02; node.y += (node.targetY - node.y) * 0.02; node.x += Math.sin(time + node.targetX * 0.01) * 0.3; node.y += Math.cos(time + node.targetY * 0.01) * 0.3; }); animRef.current = requestAnimationFrame(draw); } draw(); return () => cancelAnimationFrame(animRef.current); }, [entities, highlightedEntities, activeFilter]); const handleMouseMove = useCallback((e) => { const canvas = canvasRef.current; const rect = canvas.getBoundingClientRect(); const x = (e.clientX - rect.left) * 2; const y = (e.clientY - rect.top) * 2; const nodes = nodesRef.current; let found = null; for (const node of nodes) { const dx = node.x - x; const dy = node.y - y; if (Math.sqrt(dx * dx + dy * dy) < node.r * 1.5) { found = node.id; break; } } hoveredRef.current = found; setHovered(found); canvas.style.cursor = found ? "pointer" : "default"; }, []); const handleClick = useCallback((e) => { if (hoveredRef.current && onNodeClick) { const node = nodesRef.current.find((n) => n.id === hoveredRef.current); if (node) onNodeClick(node); } }, [onNodeClick]); return (
{hovered && (() => { const node = nodesRef.current.find((n) => n.id === hovered); if (!node) return null; const canvas = canvasRef.current; const rect = canvas.getBoundingClientRect(); const sx = node.x / 2; const sy = node.y / 2; return (
{node.icon} {node.label}
{Object.entries(node.props || {}).map(([k, v]) => (
{k}: {String(v)}
))}
); })()}
); } // ── Typewriter Effect ──────────────────────────────────────────────── function Typewriter({ text, speed = 12, onDone }) { const [displayed, setDisplayed] = useState(""); const idx = useRef(0); useEffect(() => { idx.current = 0; setDisplayed(""); const interval = setInterval(() => { idx.current++; if (idx.current >= text.length) { setDisplayed(text); clearInterval(interval); onDone && onDone(); } else { setDisplayed(text.slice(0, idx.current)); } }, speed); return () => clearInterval(interval); }, [text, speed]); return {displayed}; } // ── Main App ───────────────────────────────────────────────────────── export default function TrustGraphRetailDemo() { const [activeTab, setActiveTab] = useState("graph"); const [activeFilter, setActiveFilter] = useState(null); const [selectedQuery, setSelectedQuery] = useState(null); const [queryPhase, setQueryPhase] = useState("idle"); // idle, thinking, answering, done const [thinkingStep, setThinkingStep] = useState(0); const [selectedNode, setSelectedNode] = useState(null); const [showOntology, setShowOntology] = useState(false); const runQuery = (idx) => { setSelectedQuery(idx); setQueryPhase("thinking"); setThinkingStep(0); const q = DEMO_QUERIES[idx]; let step = 0; const interval = setInterval(() => { step++; if (step >= q.thinking.length) { clearInterval(interval); setTimeout(() => setQueryPhase("answering"), 400); } setThinkingStep(step); }, 800); }; const highlightedEntities = selectedQuery !== null && queryPhase !== "idle" ? DEMO_QUERIES[selectedQuery].entities : selectedNode ? [selectedNode.id, ...RELATIONSHIPS.filter(r => r.from === selectedNode.id || r.to === selectedNode.id).map(r => r.from === selectedNode.id ? r.to : r.from)] : []; return (
{/* ── Header ────────────────────────────────────────── */}
TG
TrustGraph
RETAIL INTELLIGENCE PLATFORM
{["graph", "query", "ontology"].map((tab) => ( ))}
{/* ── Domain Filter Bar ──────────────────────────────── */} {activeTab === "graph" && (
FILTER: {Object.entries(ONTOLOGY).map(([key, data]) => ( ))}
{getAllEntities().length} entities · {RELATIONSHIPS.length} relationships
)} {/* ── Main Content ──────────────────────────────────── */}
{/* ── Graph View ──────────────────────────────────── */} {activeTab === "graph" && ( <>
setSelectedNode(selectedNode?.id === node.id ? null : node)} activeFilter={activeFilter} />
{/* Side panel for selected node */} {selectedNode && (
{ONTOLOGY[selectedNode.domain].label.toUpperCase()} ENTITY
{selectedNode.icon} {selectedNode.label}
PROPERTIES
{Object.entries(selectedNode.props || {}).map(([k, v]) => (
{k} {String(v)}
))}
RELATIONSHIPS
{RELATIONSHIPS.filter(r => r.from === selectedNode.id || r.to === selectedNode.id).map((r, i) => { const otherId = r.from === selectedNode.id ? r.to : r.from; const other = getAllEntities().find(e => e.id === otherId); const direction = r.from === selectedNode.id ? "→" : "←"; return (
{ const n = getAllEntities().find(e => e.id === otherId); if (n) setSelectedNode(n); }}>
{direction} {other?.label}
{r.predicate.replace(/_/g, " ")} · strength: {r.strength}
); })}
)} )} {/* ── Agent Query View ────────────────────────────── */} {activeTab === "query" && (
{/* Query selector */}
SELECT A QUERY TO SEE GRAPH-POWERED AGENT INTELLIGENCE
{DEMO_QUERIES.map((dq, idx) => ( ))}
{/* Response area */} {selectedQuery !== null && (
{/* Graph traversal steps */} {(queryPhase === "thinking" || queryPhase === "answering" || queryPhase === "done") && (
◈ GRAPH TRAVERSAL
{DEMO_QUERIES[selectedQuery].thinking.map((step, i) => (
{i < thinkingStep && } {step}
))} {queryPhase === "thinking" && (
Traversing graph...
)}
)} {/* Answer */} {(queryPhase === "answering" || queryPhase === "done") && (
AGENT RESPONSE
{DEMO_QUERIES[selectedQuery].triples} triples traversed · {DEMO_QUERIES[selectedQuery].entities.length} entities resolved
setQueryPhase("done")} />
)}
)}
{/* Graph visualization alongside query */}
{}} activeFilter={null} />
)} {/* ── Ontology View ──────────────────────────────── */} {activeTab === "ontology" && (
ONTOLOGY SCHEMA · RETAIL INTELLIGENCE DOMAIN
{/* Ontology class cards */}
{Object.entries(ONTOLOGY).map(([key, data]) => (
{data.icon}
{data.label}
owl:Class
{data.description}
PROPERTIES
{data.properties.map((p) => ( {p} ))}
INSTANCES ({data.subclasses.length})
{data.subclasses.map((sc) => (
{sc.label} {sc.id}
))}
))}
{/* Relationship predicates */}
RELATIONSHIP PREDICATES
{[...new Set(RELATIONSHIPS.map(r => r.predicate))].map((pred) => { const sample = RELATIONSHIPS.find(r => r.predicate === pred); const fromDomain = sample.domain[0]; const toDomain = sample.domain[1]; return (
{pred.replace(/_/g, " ")}
{ONTOLOGY[fromDomain].label} {" → "} {ONTOLOGY[toDomain].label}
); })}
{/* Triple count summary */}
{[ { label: "Classes", value: 4 }, { label: "Instances", value: getAllEntities().length }, { label: "Predicates", value: [...new Set(RELATIONSHIPS.map(r => r.predicate))].length }, { label: "Triples", value: RELATIONSHIPS.length }, ].map((s) => (
{s.value}
{s.label.toUpperCase()}
))}
)}
{/* ── Bottom Status Bar ──────────────────────────────── */}
◈ Ontology: Consumer × Agent × Retail × Brand ⬡ GraphRAG: Active ⚡ Agent Orchestration: Online
Context Graph Connected | trustgraph.ai
); }