import { useState, useEffect } from 'react'; import { useRouter } from 'next/router'; import Head from 'next/head'; import { toast } from 'react-hot-toast'; import { MagnifyingGlassIcon, PhoneIcon, ChevronLeftIcon, ChevronRightIcon, PlayIcon, StopIcon, EyeIcon } from '@heroicons/react/24/outline'; import { supabase, TABLES } from '@/lib/supabase'; import { getDemoData } from '@/lib/demo-data'; import { Call } from '@/types'; import { formatTime } from '@/utils'; import Layout from '@/components/Layout'; interface CallFilters { search: string; status: 'all' | 'pending' | 'active' | 'ended' | 'cancelled' | 'failed'; call_type: 'all' | 'audio' | 'video'; call_mode: 'all' | 'ai_voice' | 'ai_video' | 'sign_language' | 'human_interpreter'; sortBy: 'created_at' | 'duration' | 'cost'; sortOrder: 'asc' | 'desc'; } export default function CallsPage() { const [calls, setCalls] = useState([]); const [loading, setLoading] = useState(true); const [currentPage, setCurrentPage] = useState(1); const [totalPages, setTotalPages] = useState(1); const [totalCount, setTotalCount] = useState(0); const [isDemoMode, setIsDemoMode] = useState(false); const [filters, setFilters] = useState({ search: '', status: 'all', call_type: 'all', call_mode: 'all', sortBy: 'created_at', sortOrder: 'desc' }); const router = useRouter(); const pageSize = 20; // 获取通话记录列表 const fetchCalls = async (page = 1) => { try { setLoading(true); // 检查是否为演示模式 const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL; const isDemo = !supabaseUrl || supabaseUrl === 'https://demo.supabase.co' || supabaseUrl === ''; setIsDemoMode(isDemo); if (isDemo) { // 使用演示数据 const result = await getDemoData.calls(); // 转换数据格式以匹配 Call 类型 const formattedResult = result.map(item => ({ ...item, caller_id: item.user_id, callee_id: item.interpreter_id, call_type: 'audio' as const, call_mode: 'human_interpreter' as const, end_time: item.end_time || undefined, room_sid: undefined, twilio_call_sid: undefined, quality_rating: undefined, currency: 'CNY' as const, updated_at: item.created_at })); setCalls(formattedResult); setTotalCount(formattedResult.length); setTotalPages(Math.ceil(formattedResult.length / pageSize)); setCurrentPage(page); } else { // 使用真实数据 let query = supabase .from(TABLES.CALLS) .select('*', { count: 'exact' }); // 搜索过滤 if (filters.search) { query = query.or(`caller_id.ilike.%${filters.search}%,callee_id.ilike.%${filters.search}%`); } // 状态过滤 if (filters.status !== 'all') { query = query.eq('status', filters.status); } // 通话类型过滤 if (filters.call_type !== 'all') { query = query.eq('call_type', filters.call_type); } // 通话模式过滤 if (filters.call_mode !== 'all') { query = query.eq('call_mode', filters.call_mode); } // 排序 query = query.order(filters.sortBy, { ascending: filters.sortOrder === 'asc' }); // 分页 const from = (page - 1) * pageSize; const to = from + pageSize - 1; query = query.range(from, to); const { data, error, count } = await query; if (error) throw error; setCalls(data || []); setTotalCount(count || 0); setTotalPages(Math.ceil((count || 0) / pageSize)); setCurrentPage(page); } } catch (error) { console.error('Error fetching calls:', error); toast.error('获取通话记录失败'); // 如果真实数据获取失败,切换到演示模式 if (!isDemoMode) { setIsDemoMode(true); const result = await getDemoData.calls(); const formattedResult = result.map(item => ({ ...item, caller_id: item.user_id, callee_id: item.interpreter_id, call_type: 'audio' as const, call_mode: 'human_interpreter' as const, end_time: item.end_time || undefined, room_sid: undefined, twilio_call_sid: undefined, quality_rating: undefined, currency: 'CNY' as const, updated_at: item.created_at })); setCalls(formattedResult); setTotalCount(formattedResult.length); setTotalPages(Math.ceil(formattedResult.length / pageSize)); setCurrentPage(page); } } finally { setLoading(false); } }; // 处理筛选变更 const handleFilterChange = (key: keyof CallFilters, value: any) => { setFilters(prev => ({ ...prev, [key]: value })); }; // 应用筛选 const applyFilters = () => { setCurrentPage(1); fetchCalls(1); }; // 重置筛选 const resetFilters = () => { setFilters({ search: '', status: 'all', call_type: 'all', call_mode: 'all', sortBy: 'created_at', sortOrder: 'desc' }); setCurrentPage(1); fetchCalls(1); }; // 获取状态颜色 const getStatusColor = (status: string) => { switch (status) { case 'active': return 'bg-green-100 text-green-800'; case 'pending': return 'bg-yellow-100 text-yellow-800'; case 'ended': return 'bg-blue-100 text-blue-800'; case 'cancelled': return 'bg-red-100 text-red-800'; case 'failed': return 'bg-red-100 text-red-800'; default: return 'bg-gray-100 text-gray-800'; } }; // 获取状态文本 const getStatusText = (status: string) => { switch (status) { case 'active': return '进行中'; case 'pending': return '待接听'; case 'ended': return '已结束'; case 'cancelled': return '已取消'; case 'failed': return '失败'; default: return '未知'; } }; // 获取通话类型文本 const getCallTypeText = (type: string) => { switch (type) { case 'audio': return '语音通话'; case 'video': return '视频通话'; default: return '未知'; } }; // 获取通话模式文本 const getCallModeText = (mode: string) => { switch (mode) { case 'ai_voice': return 'AI语音'; case 'ai_video': return 'AI视频'; case 'sign_language': return '手语翻译'; case 'human_interpreter': return '人工翻译'; default: return '未知'; } }; // 格式化时长 const formatDuration = (seconds?: number) => { if (!seconds) return '-'; const minutes = Math.floor(seconds / 60); const remainingSeconds = seconds % 60; return `${minutes}分${remainingSeconds}秒`; }; useEffect(() => { fetchCalls(); }, []); return ( 通话记录 - 口译服务管理后台
{/* 页面标题 */}

通话记录

{/* 搜索和筛选 */}
{/* 搜索框 */}
handleFilterChange('search', e.target.value)} className="block w-full pl-10 pr-3 py-2 border border-gray-300 rounded-md leading-5 bg-white placeholder-gray-500 focus:outline-none focus:placeholder-gray-400 focus:ring-1 focus:ring-blue-500 focus:border-blue-500" />
{/* 状态筛选 */}
{/* 通话类型筛选 */}
{/* 通话模式筛选 */}
{/* 排序 */}
{/* 通话记录列表 */} {loading ? (
) : calls.length === 0 ? (

暂无通话记录

调整筛选条件或检查数据源

) : (

通话记录 ({totalCount} 条记录)

{calls.map((call) => ( ))}
通话信息 类型/模式 时长 费用 状态 开始时间 操作
{call.id}
主叫: {call.caller_id}
{call.callee_id && (
被叫: {call.callee_id}
)}
{getCallTypeText(call.call_type)}
{getCallModeText(call.call_mode)}
{formatDuration(call.duration)} ¥{call.cost.toFixed(2)} {call.status === 'active' && } {call.status === 'ended' && } {getStatusText(call.status)} {formatTime(call.start_time)}
{/* 分页 */} {totalPages > 1 && (

显示第 {(currentPage - 1) * pageSize + 1} 到{' '} {Math.min(currentPage * pageSize, totalCount)} {' '} 条,共 {totalCount} 条记录

)}
)}
); }