import { useEffect, useRef, useState } from 'react'; import { Link } from 'react-router-dom'; import { ImageWithFallback } from '@/components/ImageWithFallback'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, } from '@/components/ui/dialog'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { Send, Loader2, MapPin, ChevronRight, Sparkles } from 'lucide-react'; import { cn } from '@/lib/utils'; import { useTranslation } from '@/contexts/language'; import { CREW_AGENT_AVATAR } from '@/lib/crewAgent'; import { useCrewAgentChat, type CrewContext, type CrewAgentMessage } from '@/hooks/useCrewAgentChat'; interface CrewAgentPanelProps { open: boolean; onOpenChange: (open: boolean) => void; ctx: CrewContext; } /** Turn a skate-spots.org URL into an in-app path so links stay inside the app. */ function toAppPath(url: string): string { try { return new URL(url).pathname; } catch { return url; } } interface ReceiptItem { key: string; title: string; subtitle?: string; image?: string; to: string; } /** Normalise a CVM tool result into flat receipt cards. */ function toReceiptItems(t: (key: string) => string, tool: string, data: unknown): ReceiptItem[] { const d = data as Record; const take = (arr: unknown): T[] => (Array.isArray(arr) ? (arr as T[]).slice(0, 6) : []); if (tool === 'find_spots') { return take>(d.spots).map((s, i) => ({ key: `${s.url}-${i}`, title: s.name || t('socialUi.agentPanel.spot'), subtitle: [s.difficulty, s.location].filter(Boolean).join(' · ') || undefined, image: s.image, to: toAppPath(s.url), })); } if (tool === 'find_events') { return take>(d.events).map((e, i) => ({ key: `${e.url}-${i}`, title: e.title || t('socialUi.agentPanel.event'), subtitle: e.location, image: e.image, to: toAppPath(e.url), })); } if (tool === 'find_coaches') { return take>(d.profiles).map((p, i) => ({ key: `${p.url}-${i}`, title: p.title || p.role || t('socialUi.agentPanel.labPro'), subtitle: [p.role, p.location].filter(Boolean).join(' · ') || undefined, image: p.image, to: toAppPath(p.url), })); } if (tool === 'find_gear') { return take>(d.listings).map((g, i) => ({ key: `${g.url}-${i}`, title: g.title || t('socialUi.agentPanel.gear'), subtitle: g.condition, image: g.image, to: toAppPath(g.url), })); } if (tool === 'find_crews') { return take>(d.crews).map((c, i) => ({ key: `${c.url}-${i}`, title: c.name || t('socialUi.agentPanel.crew'), subtitle: c.location, image: c.image, to: toAppPath(c.url), })); } return []; } function ReceiptCards({ tool, data, onNavigate }: { tool: string; data: unknown; onNavigate: () => void }) { const { t } = useTranslation(); const items = toReceiptItems(t, tool, data); if (items.length === 0) return null; return (
{items.map((item) => (
} />

{item.title}

{item.subtitle &&

{item.subtitle}

}
))}
); } function MessageRow({ m, onNavigate }: { m: CrewAgentMessage; onNavigate: () => void }) { const isAgent = m.role === 'agent'; return (
{isAgent && ( SQ )}
{m.text}
{isAgent && m.receipts && ( )}
); } export function CrewAgentPanel({ open, onOpenChange, ctx }: CrewAgentPanelProps) { const { t } = useTranslation(); const { messages, isThinking, send } = useCrewAgentChat(ctx); const [draft, setDraft] = useState(''); const scrollRef = useRef(null); const suggestions = [ t('socialUi.agentPanel.suggestion1'), t('socialUi.agentPanel.suggestion2'), t('socialUi.agentPanel.suggestion3'), t('socialUi.agentPanel.suggestion4'), t('socialUi.agentPanel.suggestion5'), ]; useEffect(() => { scrollRef.current?.scrollTo({ top: scrollRef.current.scrollHeight, behavior: 'smooth' }); }, [messages, isThinking]); const submit = (text: string) => { const t = text.trim(); if (!t || isThinking) return; setDraft(''); send(t); }; return ( SQ {t('socialUi.crew.askSasquatch')} {t('socialUi.crew.agent')} {t('socialUi.agentPanel.description')}
{messages.length === 0 && !isThinking && (
SQ

{t('socialUi.agentPanel.greeting', { crew: ctx.crewName })}

{suggestions.map((s) => ( ))}
)} {messages.map((m) => ( onOpenChange(false)} /> ))} {isThinking && (
{t('socialUi.agentPanel.thinking')}
)}
{ e.preventDefault(); submit(draft); }} className="flex items-center gap-2" > setDraft(e.target.value)} placeholder={t('socialUi.agentPanel.placeholder')} maxLength={300} className="flex-1" />

{t('socialUi.agentPanel.disclaimer')}

); }