"use client"; import { useEffect, useRef, useState } from "react"; import Link from "next/link"; import { AdminCard } from "@/components/admin/design-system"; import { setAIProviderSettings } from "@/actions/integrations/ai-providers"; // ── Header Icon Component ───────────────────────────────────────────────────── function AIHeaderIcon() { return (
); } // ── Tool Card Component ─────────────────────────────────────────────────────── type ToolCardProps = { tool: AITool; isConnected: boolean; onOpen: (tool: AITool) => void; }; function ToolCard({ tool, isConnected, onOpen }: ToolCardProps) { const isReady = tool.status === "available" && isConnected; return (
{tool.icon}

{tool.title}

{tool.badge && ( {tool.badge} )} {tool.status === "available" && ( {isConnected ? "Ready" : "Disabled"} )} {tool.status === "experimental" && ( Experimental )} {tool.status === "coming_soon" && ( Coming Soon )}
{tool.module}

{tool.description}

{tool.status === "available" && ( )} {tool.status === "coming_soon" && ( )} {tool.status === "experimental" && ( )}
); } // ── Connection Status Banner ─────────────────────────────────────────────────── function ConnectionStatusBanner({ isConnected, availableCount, onConfigure }: { isConnected: boolean; availableCount: number; onConfigure: () => void; }) { if (isConnected) { return (

AI Connected

{availableCount} tool{availableCount !== 1 ? "s" : ""} ready to use

Active
); } return (

AI Not Configured

Add your API key to enable AI tools.

); } type ToolStatus = "available" | "coming_soon" | "experimental"; type AITool = { id: string; title: string; description: string; icon: string; module: string; status: ToolStatus; badge?: string; }; const AI_TOOLS: AITool[] = [ { id: "campaign-writer", title: "Campaign Idea Generator", description: "Generate email campaign topics, angles, and subject lines based on your products, season, and customer segments.", icon: "📧", module: "Harvest Reach", status: "available", badge: "New", }, { id: "product-writer", title: "Product Description Writer", description: "AI-assisted product descriptions, pricing analysis, and image alt-text generation for your catalog.", icon: "🛒", module: "Products", status: "available", }, { id: "route-suggester", title: "Route Optimizer", description: "Get AI-powered suggestions for stop ordering, grouping, and delivery sequence to minimize drive time.", icon: "🗺️", module: "Stops & Routes", status: "available", }, { id: "customer-insights", title: "Customer Insights", description: "Natural language queries across orders and customers. Ask 'Which customers haven't ordered in 30 days?' or 'What products are trending this month?'", icon: "🔍", module: "Reports", status: "available", }, { id: "stop-blast-advisor", title: "Stop Blast Advisor", description: "AI suggestions for timing, content, and audience targeting for operational stop blast messages.", icon: "📢", module: "Harvest Reach", status: "available", }, { id: "pricing-advisor", title: "Pricing Advisor", description: "Analyze demand, seasonality, and historical sales to suggest optimal product prices for your market.", icon: "💰", module: "Products", status: "available", }, { id: "report-explainer", title: "Report Explainer", description: "Plain-English summaries of complex reports. Just ask 'why did sales drop this week?' and get an AI-generated breakdown.", icon: "📊", module: "Reports", status: "available", }, { id: "demand-forecast", title: "Demand Forecasting", description: "Predict order volumes and popular products for upcoming stops based on historical trends and seasonal patterns.", icon: "📈", module: "Orders", status: "available", }, ]; type Provider = "openai" | "anthropic" | "google" | "xai" | "custom"; type Props = { isConnected: boolean; brandId: string; brandName: string; provider?: Provider; model?: string; customEndpoint?: string; }; // ── Provider Selector ───────────────────────────────────────────────────────── type ProviderOption = { id: Provider; name: string; icon: string; description: string; }; const PROVIDERS: ProviderOption[] = [ { id: "openai", name: "OpenAI", icon: "💩", description: "GPT-4, GPT-4o, o1" }, { id: "anthropic", name: "Anthropic", icon: "🧠", description: "Claude 3.5 Sonnet, Opus" }, { id: "google", name: "Google", icon: "🔴", description: "Gemini 2.0 Flash, Pro" }, { id: "xai", name: "xAI", icon: "⚡", description: "Grok-2, Grok-1.5" }, { id: "custom", name: "Custom", icon: "🔧", description: "Bring your own endpoint" }, ]; // ── Model Input ─────────────────────────────────────────────────────────────── function ModelInput({ currentModel, brandId, onChange }: { currentModel: string; brandId: string; onChange: (model: string) => void; }) { // Uncontrolled input: the editable buffer is the DOM input itself. // The parent uses `key={currentModel}` so that when the prop changes // externally, this component fully remounts and `defaultValue` is // re-applied from the new prop — no stale copy. const inputRef = useRef(null); const [saving, setSaving] = useState(false); const [saved, setSaved] = useState(false); const [error, setError] = useState(null); const handleSave = async () => { const value = inputRef.current?.value ?? ""; if (!value.trim()) return; setSaving(true); setError(null); const result = await setAIProviderSettings(brandId, { model: value.trim() }); if (result.success) { onChange(value.trim()); setSaved(true); setTimeout(() => setSaved(false), 2000); } else { setError(result.error ?? "Failed to save"); } setSaving(false); }; return (
🤖

Model

⚠️ Model ID must be exact — check the provider's documentation for the correct identifier.

{error &&

{error}

}
); } // ── Provider Selector Component ───────────────────────────────────────────── function ProviderSelector({ currentProvider, currentModel, brandId, onSelect }: { currentProvider: Provider; currentModel: string; brandId: string; onSelect: (provider: Provider) => void; }) { const handleSelect = async (provider: Provider) => { onSelect(provider); await setAIProviderSettings(brandId, { provider }); }; return (
💩

AI Provider

{PROVIDERS.map((provider) => ( ))}

Current model: {currentModel}

); } // ── Form Input Styles (Shared) ──────────────────────────────────────────────── const inputBaseClass = "w-full rounded-xl px-4 py-2.5 text-sm outline-none transition-colors"; const inputStyle = { border: '1px solid var(--admin-border)', backgroundColor: 'var(--admin-card-bg)', color: 'var(--admin-text-primary)', }; const inputFocusStyle = { borderColor: 'var(--admin-accent)', boxShadow: '0 0 0 3px rgba(202, 117, 67, 0.1)', }; const textareaStyle = { ...inputStyle }; const textareaFocusStyle = { ...inputFocusStyle }; const btnPrimaryClass = "rounded-xl px-5 py-2.5 text-sm font-bold text-white transition-all"; const btnPrimaryStyle = { background: 'linear-gradient(135deg, #ca7543 0%, #d4865a 100%)', boxShadow: '0 2px 8px rgba(202, 117, 67, 0.25)', }; const labelStyle = { display: 'block', fontSize: '0.875rem', fontWeight: 500, marginBottom: '0.25rem', color: 'var(--admin-text-primary)', }; // ── Campaign Writer Tool ────────────────────────────────────────────────────── function CampaignWriterTool({ brandId, brandName }: { brandId: string; brandName: string }) { const [topic, setTopic] = useState(""); const [loading, setLoading] = useState(false); const [results, setResults] = useState<{ angle: string; subject: string; body: string }[]>([]); const [error, setError] = useState(null); async function handleGenerate() { if (!topic.trim()) return; setLoading(true); setError(null); setResults([]); try { const res = await fetch("/api/ai/campaign-writer", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ topic, brandId, brandName }), }); const data = await res.json(); if (!res.ok) throw new Error(data.error ?? "Generation failed"); setResults(data.ideas ?? []); } catch (err) { setError(err instanceof Error ? err.message : "Generation failed"); } finally { setLoading(false); } } return (