- Build complete Next.js CRM for commercial real estate - Add authentication with JWT sessions and role-based access - Add GoHighLevel API integration for contacts, conversations, opportunities - Add AI-powered Control Center with tool calling - Add Setup page with onboarding checklist (/setup) - Add sidebar navigation with Setup menu item - Fix type errors in onboarding API, GHL services, and control center tools - Add Prisma schema with SQLite for local development - Add UI components with clay morphism design system Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
137 lines
4.2 KiB
TypeScript
137 lines
4.2 KiB
TypeScript
'use client';
|
|
|
|
import React, { useState } from 'react';
|
|
import { CheckCircle, XCircle, ChevronDown, ChevronUp, FileCode } from 'lucide-react';
|
|
import type { ToolResult } from '@/types/control-center';
|
|
import { cn } from '@/lib/utils';
|
|
|
|
interface ToolResultCardProps {
|
|
toolResult: ToolResult;
|
|
}
|
|
|
|
// Threshold for auto-collapsing large results
|
|
const LARGE_RESULT_THRESHOLD = 500;
|
|
|
|
export const ToolResultCard: React.FC<ToolResultCardProps> = ({ toolResult }) => {
|
|
const isError = !toolResult.success;
|
|
const resultData = isError ? toolResult.error : toolResult.result;
|
|
|
|
// Determine if result is large and should be collapsed by default
|
|
const resultString = typeof resultData === 'string'
|
|
? resultData
|
|
: JSON.stringify(resultData, null, 2);
|
|
const isLargeResult = resultString.length > LARGE_RESULT_THRESHOLD;
|
|
|
|
const [isExpanded, setIsExpanded] = useState(!isLargeResult);
|
|
|
|
const formatResult = (data: any): string => {
|
|
if (data === null || data === undefined) {
|
|
return 'No result data';
|
|
}
|
|
if (typeof data === 'string') {
|
|
return data;
|
|
}
|
|
return JSON.stringify(data, null, 2);
|
|
};
|
|
|
|
return (
|
|
<div
|
|
className={cn(
|
|
'bg-[#E8EDF2] rounded-2xl border-2',
|
|
'shadow-[4px_4px_8px_#c5c9d1,-4px_-4px_8px_#ffffff]',
|
|
'transition-all duration-300',
|
|
isError ? 'border-danger-200' : 'border-success-200'
|
|
)}
|
|
>
|
|
{/* Header */}
|
|
<div
|
|
className="flex items-center gap-3 p-4 cursor-pointer"
|
|
onClick={() => setIsExpanded(!isExpanded)}
|
|
>
|
|
{/* Status Icon */}
|
|
<div
|
|
className={cn(
|
|
'p-2.5 rounded-xl',
|
|
isError
|
|
? 'bg-danger-100 text-danger-600'
|
|
: 'bg-success-100 text-success-600'
|
|
)}
|
|
>
|
|
{isError ? <XCircle size={18} /> : <CheckCircle size={18} />}
|
|
</div>
|
|
|
|
{/* Result Info */}
|
|
<div className="flex-1 min-w-0">
|
|
<div className="flex items-center gap-2">
|
|
<FileCode size={14} className="text-gray-400" />
|
|
<span className="text-sm font-medium text-gray-600">
|
|
Tool Result
|
|
</span>
|
|
</div>
|
|
<div className="flex items-center gap-2 mt-0.5">
|
|
<span
|
|
className={cn(
|
|
'text-xs font-semibold px-2 py-0.5 rounded-full',
|
|
isError
|
|
? 'bg-danger-100 text-danger-700'
|
|
: 'bg-success-100 text-success-700'
|
|
)}
|
|
>
|
|
{isError ? 'Error' : 'Success'}
|
|
</span>
|
|
{!isExpanded && (
|
|
<span className="text-xs text-gray-500 truncate">
|
|
{resultString.substring(0, 50)}
|
|
{resultString.length > 50 ? '...' : ''}
|
|
</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Expand/Collapse Toggle */}
|
|
<button
|
|
className={cn(
|
|
'p-1.5 rounded-lg transition-colors',
|
|
'hover:bg-gray-200 text-gray-500'
|
|
)}
|
|
aria-label={isExpanded ? 'Collapse result' : 'Expand result'}
|
|
>
|
|
{isExpanded ? <ChevronUp size={16} /> : <ChevronDown size={16} />}
|
|
</button>
|
|
</div>
|
|
|
|
{/* Collapsible Result Content */}
|
|
{isExpanded && (
|
|
<div className="px-4 pb-4">
|
|
<div
|
|
className={cn(
|
|
'rounded-xl p-3',
|
|
'shadow-[inset_2px_2px_4px_rgba(0,0,0,0.05),inset_-2px_-2px_4px_rgba(255,255,255,0.7)]',
|
|
isError ? 'bg-danger-50' : 'bg-[#F5F8FA]'
|
|
)}
|
|
>
|
|
<p
|
|
className={cn(
|
|
'text-xs font-medium uppercase tracking-wide mb-2',
|
|
isError ? 'text-danger-600' : 'text-gray-500'
|
|
)}
|
|
>
|
|
{isError ? 'Error Details' : 'Result Data'}
|
|
</p>
|
|
<pre
|
|
className={cn(
|
|
'text-xs overflow-x-auto whitespace-pre-wrap break-words font-mono max-h-64 overflow-y-auto',
|
|
isError ? 'text-danger-700' : 'text-gray-700'
|
|
)}
|
|
>
|
|
{formatResult(resultData)}
|
|
</pre>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default ToolResultCard;
|