Getting Started
Components
✓ Connected to Infactory
Project:
6b107ccf-44b1-42cc-a03a-8967d9448d95"use client"
import React, { useState } from "react"
import { LayoutGrid } from "lucide-react"
import { useInfactoryFetch } from "@/hooks/use-infactory-fetch"
import { FacetState } from "@/lib/facets/base/base-facet"
import {
SampleQueriesControlFacetV2,
type SampleQuery,
} from "@/lib/facets/implementations/control/sample_queries_control_facet"
// Read from environment variables
const INFACTORY_CONFIG = {
projectId: process.env.NEXT_PUBLIC_INFACTORY_PROJECT_ID || "",
apiKey: process.env.NEXT_PUBLIC_INFACTORY_API_KEY || "",
apiUrl: process.env.NEXT_PUBLIC_INFACTORY_API_URL || "http://localhost:8000",
}
export function SampleQueriesDemo() {
const [lastQuery, setLastQuery] = useState<SampleQuery | null>(null)
const isConfigured = INFACTORY_CONFIG.projectId && INFACTORY_CONFIG.apiKey
// Fetch StructuredData from Infactory API
const { structuredData, isLoading, error } = useInfactoryFetch({
projectId: INFACTORY_CONFIG.projectId,
apiKey: INFACTORY_CONFIG.apiKey,
apiUrl: INFACTORY_CONFIG.apiUrl,
program: "video_sample_queries",
enabled: !!isConfigured, // Only fetch if configured
})
// Create facet instance
const facet = React.useMemo(
() =>
new SampleQueriesControlFacetV2({
maxQueries: 4,
key_column: "nf:col/video_sample_queries/asset_id",
value_column: "nf:col/video_sample_queries/summary",
click_api_path: "custom_query",
click_key_param: "asset_id",
click_value_param: "summary",
accessoryButton: {
label: "Show Overview",
icon: LayoutGrid,
apiTemplate: {
mode: "direct",
api_path: "show_overview",
},
},
}),
[]
)
// Handle API calls from facet
const handleAPICall = React.useCallback(async (apiCall: any) => {
console.log("API call:", apiCall)
// Extract query info from API params
if (apiCall.api_params) {
const query: SampleQuery = {
id: apiCall.api_params.asset_id || "",
text: apiCall.api_params.summary || "",
}
setLastQuery(query)
}
}, [])
return (
<div className="w-full space-y-4">
{!isConfigured ? (
<div className="rounded-lg border border-amber-200 bg-amber-50/50 p-4 text-sm">
<div className="font-medium text-amber-900">
Configuration Required
</div>
<div className="mt-2 text-amber-700">
Set these environment variables to connect:
</div>
<div className="mt-2 rounded bg-amber-100 p-2 font-mono text-xs text-amber-800">
NEXT_PUBLIC_INFACTORY_PROJECT_ID=your-project-id
<br />
NEXT_PUBLIC_INFACTORY_API_KEY=your-api-key
<br />
NEXT_PUBLIC_INFACTORY_API_URL=http://localhost:8000
</div>
</div>
) : error ? (
<div className="rounded-lg border border-red-200 bg-red-50/50 p-4 text-sm">
<div className="font-medium text-red-900">Error</div>
<div className="mt-2 text-red-700">{error}</div>
</div>
) : (
<div className="rounded-lg border border-green-200 bg-green-50/50 p-4 text-sm">
<div className="font-medium text-green-900">
✓ Connected to Infactory
</div>
<div className="text-green-700">
Project:{" "}
<code className="text-xs">{INFACTORY_CONFIG.projectId}</code>
</div>
</div>
)}
{/* Render the facet */}
{facet.render({
responseData: structuredData ?? undefined,
isRequestInProgress: isLoading,
onAPICall: handleAPICall,
facetState: FacetState.ENABLED,
})}
{lastQuery && (
<div className="bg-muted/50 rounded-lg border p-4 text-sm">
<div className="font-medium">Last selected query:</div>
<div className="text-muted-foreground mt-1">{lastQuery.text}</div>
<div className="text-muted-foreground mt-1 text-xs">
ID: {lastQuery.id}
</div>
</div>
)}
</div>
)
}
Installation
pnpm dlx shadcn@latest add https://ui.infactory.ai//r/sample-queries.json
This will install the facet implementation, API client, and all dependencies.
Folder structure
hooks
use-infactory-fetch.ts
lib
facets
api-utils.ts
base
base-control-facet.ts
base-facet.ts
base-renderer-facet.ts
hooks
use-facet-notifications.ts
implementations
control
sample-queries-control-facet.tsx
notification-manager.ts
structured-data.ts
types.ts
infactory
client.ts
use-infactory-fetch.ts
"use client"/** * React hook for fetching StructuredData from Infactory API * * This hook simplifies the process of: * 1. Creating an Infactory client * 2. Executing a query program * 3. Parsing the SSE stream * 4. Returning StructuredData ready for facets * * Usage: * ```tsx * const { structuredData, isLoading, error, refetch } = useInfactoryFetch({ * projectId: "my-project", * apiKey: "my-key", * program: "video_sample_queries", * params: { limit: 10 } * }) * * // Use structuredData with any facet * const facet = new SampleQueriesControlFacetV2(config) * return facet.render({ * responseData: structuredData, * isRequestInProgress: isLoading, * ... * }) * ``` */import { useCallback, useEffect, useState } from "react"import type { StructuredData } from "@/lib/facets/structured-data"import { createInfactoryClient } from "@/lib/infactory/client"export interface UseInfactoryFetchOptions { // Infactory credentials (required) projectId: string apiKey: string apiUrl?: string // Query program to execute program: string params?: Record<string, any> // Dialog options dialogName?: string // Auto-fetch on mount (default: true) enabled?: boolean}export interface UseInfactoryFetchResult { // The StructuredData ready for facets structuredData: StructuredData | null // Loading state isLoading: boolean // Error message if any error: string | null // Manually trigger a refetch refetch: () => void}/** * Hook to fetch StructuredData from Infactory API */export function useInfactoryFetch( options: UseInfactoryFetchOptions): UseInfactoryFetchResult { const { projectId, apiKey, apiUrl, program, params, dialogName, enabled = true, } = options const [structuredData, setStructuredData] = useState<StructuredData | null>( null ) const [isLoading, setIsLoading] = useState(false) const [error, setError] = useState<string | null>(null) const fetchData = useCallback(async () => { if (!projectId || !apiKey || !program) { setError("projectId, apiKey, and program are required") return } setIsLoading(true) setError(null) try { const client = createInfactoryClient({ projectId, apiKey, apiUrl, }) const result = await client.run(program, params, dialogName) if (result.error) { setError(result.error) setStructuredData(null) } else { setStructuredData(result.structuredData) setError(null) } } catch (err) { const errorMessage = err instanceof Error ? err.message : "Failed to fetch data" setError(errorMessage) setStructuredData(null) } finally { setIsLoading(false) } }, [projectId, apiKey, apiUrl, program, JSON.stringify(params), dialogName]) useEffect(() => { if (enabled) { fetchData() } }, [enabled, fetchData]) return { structuredData, isLoading, error, refetch: fetchData, }}Overview
This component works anywhere - just provide your Infactory credentials and it fetches sample queries directly from your project!
Key Features
- Simple Setup - Just provide
projectIdandapiKey - Minimal Dependencies - Only requires
lucide-react - Works Anywhere - Any React app, not just Infactory Workshop
- Direct API Integration - Fetches data directly from Infactory dialog API
Usage
Basic Setup
import { SampleQueries } from "@/components/ui/sample-queries"
import { LayoutGrid } from "lucide-react"
export default function MyPage() {
const handleQuerySelect = (query) => {
console.log("Selected:", query.text)
// Execute the query in your app
}
return (
<SampleQueries
projectId={process.env.NEXT_PUBLIC_INFACTORY_PROJECT_ID!}
apiKey={process.env.NEXT_PUBLIC_INFACTORY_API_KEY!}
apiUrl="http://localhost:8000"
maxQueries={4}
onQuerySelect={handleQuerySelect}
/>
)
}With Environment Variables
# .env.local
NEXT_PUBLIC_INFACTORY_PROJECT_ID=your-project-id
NEXT_PUBLIC_INFACTORY_API_KEY=your-api-key
NEXT_PUBLIC_INFACTORY_API_URL=http://localhost:8000const INFACTORY_CONFIG = {
projectId: process.env.NEXT_PUBLIC_INFACTORY_PROJECT_ID!,
apiKey: process.env.NEXT_PUBLIC_INFACTORY_API_KEY!,
apiUrl: process.env.NEXT_PUBLIC_INFACTORY_API_URL,
}
<SampleQueries {...INFACTORY_CONFIG} />With Accessory Button
import { LayoutGrid } from "lucide-react"
<SampleQueries
projectId={projectId}
apiKey={apiKey}
accessoryButton={{
label: "Show Overview",
icon: LayoutGrid,
onClick: () => console.log("Overview clicked"),
}}
onQuerySelect={(query) => console.log(query)}
/>API Reference
SampleQueries
A component that fetches AI-generated sample queries directly from Infactory.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| projectId | string | - | Required. Your Infactory project ID |
| apiKey | string | - | Required. Your Infactory API key |
| apiUrl | string | "http://localhost:8000" | Infactory API base URL |
| maxQueries | number | 4 | Maximum number of queries to display |
| accessoryButton | AccessoryButtonConfig | - | Optional accessory button (e.g., "Show Overview") |
| onQuerySelect | (query: SampleQuery) => void | - | Callback when a query is selected |
| className | string | - | Optional CSS class name |
SampleQuery Interface
interface SampleQuery {
id: string
text: string
}AccessoryButtonConfig Interface
interface AccessoryButtonConfig {
label: string
icon: LucideIcon
onClick: () => void
}Features
- Easy Setup - Just provide credentials like ElevenLabs components
- Direct API Integration - Raw fetch calls to Infactory dialog API
- Refresh Button - Get new AI-generated queries
- Responsive Layout - 1 column mobile, 2 columns desktop
- Accessory Button - Optional action button (e.g., "Show Overview")
- Loading States - Spinner while fetching
- Error Handling - Graceful error display with retry
- TypeScript - Full type safety
Getting Your Infactory Credentials
- Sign up at platform.infactory.ai
- Create a project or use an existing one
- Get your Project ID from the project settings
- Generate an API key from your account settings
- Set environment variables and start using the component!
Example: Complete Integration
"use client"
import { useState } from "react"
import { SampleQueries, type SampleQuery } from "@/components/ui/sample-queries"
import { LayoutGrid } from "lucide-react"
export default function DashboardPage() {
const [selectedQuery, setSelectedQuery] = useState<SampleQuery | null>(null)
const handleQuerySelect = async (query: SampleQuery) => {
setSelectedQuery(query)
// Execute the query in your app
// For example: navigate, search, trigger action, etc.
await executeQuery(query.text)
}
const handleShowOverview = () => {
// Navigate to overview page or show modal
window.location.href = "/overview"
}
return (
<div className="container mx-auto p-4">
<h1 className="text-2xl font-bold mb-4">Dashboard</h1>
<SampleQueries
projectId={process.env.NEXT_PUBLIC_INFACTORY_PROJECT_ID!}
apiKey={process.env.NEXT_PUBLIC_INFACTORY_API_KEY!}
maxQueries={4}
accessoryButton={{
label: "Show Overview",
icon: LayoutGrid,
onClick: handleShowOverview,
}}
onQuerySelect={handleQuerySelect}
/>
{selectedQuery && (
<div className="mt-4 p-4 border rounded">
<h2>Selected Query</h2>
<p>{selectedQuery.text}</p>
</div>
)}
</div>
)
}Notes
- Works anywhere - Any React app, not just Infactory Workshop
- No SDK required - Uses standard fetch API for maximum compatibility
- Similar to ElevenLabs - Just provide credentials and it works!
- Dialog-based - Uses Infactory's dialog API to execute query programs
- Refresh functionality - Automatically handles fetching new queries
- Production ready - Includes loading states, error handling, and retry logic