Frontend start to use backend CRUD api
This commit is contained in:
@@ -1,18 +1,36 @@
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Download, Search, Calendar, Filter, MessageSquare, Mic, Video, Eye, X, Play, User, Bot, Clock } from 'lucide-react';
|
||||
import { Button, Input, TableHeader, TableRow, TableHead, TableCell, Badge, Drawer } from '../components/UI';
|
||||
import { mockCallLogs } from '../services/mockData';
|
||||
import { CallLog, InteractionType } from '../types';
|
||||
import { fetchHistory, fetchHistoryDetail } from '../services/backendApi';
|
||||
|
||||
export const HistoryPage: React.FC = () => {
|
||||
const [logs] = useState(mockCallLogs);
|
||||
const [logs, setLogs] = useState<CallLog[]>([]);
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [statusFilter, setStatusFilter] = useState<'all' | 'connected' | 'missed'>('all');
|
||||
const [sourceFilter, setSourceFilter] = useState<'all' | 'debug' | 'external'>('all');
|
||||
const [typeFilter, setTypeFilter] = useState<'all' | InteractionType>('all');
|
||||
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [selectedLog, setSelectedLog] = useState<CallLog | null>(null);
|
||||
const [isDetailLoading, setIsDetailLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const loadHistory = async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const list = await fetchHistory();
|
||||
setLogs(list);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
alert('加载历史记录失败,请检查后端服务。');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
loadHistory();
|
||||
}, []);
|
||||
|
||||
const filteredLogs = logs.filter(log => {
|
||||
const matchesSearch = log.agentName.toLowerCase().includes(searchTerm.toLowerCase());
|
||||
@@ -34,7 +52,7 @@ export const HistoryPage: React.FC = () => {
|
||||
log.startTime,
|
||||
log.duration
|
||||
].join(','));
|
||||
const csvContent = "data:text/csv;charset=utf-8," + [headers.join(','), ...headers.join(',')].join('\n');
|
||||
const csvContent = "data:text/csv;charset=utf-8," + [headers.join(','), ...rows].join('\n');
|
||||
const encodedUri = encodeURI(csvContent);
|
||||
const link = document.createElement("a");
|
||||
link.setAttribute("href", encodedUri);
|
||||
@@ -44,6 +62,20 @@ export const HistoryPage: React.FC = () => {
|
||||
document.body.removeChild(link);
|
||||
};
|
||||
|
||||
const openDetail = async (log: CallLog) => {
|
||||
setSelectedLog(log);
|
||||
setIsDetailLoading(true);
|
||||
try {
|
||||
const detail = await fetchHistoryDetail(log.id, log);
|
||||
setSelectedLog(detail);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
alert('加载通话详情失败。');
|
||||
} finally {
|
||||
setIsDetailLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-6 animate-in fade-in py-4 pb-10">
|
||||
<div className="flex items-center justify-between">
|
||||
@@ -118,8 +150,8 @@ export const HistoryPage: React.FC = () => {
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<tbody>
|
||||
{filteredLogs.map(log => (
|
||||
<TableRow key={log.id} className="cursor-pointer hover:bg-white/5 group" onClick={() => setSelectedLog(log)}>
|
||||
{!isLoading && filteredLogs.map(log => (
|
||||
<TableRow key={log.id} className="cursor-pointer hover:bg-white/5 group" onClick={() => openDetail(log)}>
|
||||
<TableCell className="font-mono text-xs text-muted-foreground group-hover:text-primary transition-colors">#{log.id}</TableCell>
|
||||
<TableCell className="font-medium text-white group-hover:text-primary transition-colors flex items-center gap-2">
|
||||
{log.agentName}
|
||||
@@ -144,11 +176,16 @@ export const HistoryPage: React.FC = () => {
|
||||
<TableCell className="text-muted-foreground">{log.duration}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
{filteredLogs.length === 0 && (
|
||||
{!isLoading && filteredLogs.length === 0 && (
|
||||
<TableRow>
|
||||
<TableCell colSpan={7} className="text-center py-6 text-muted-foreground">暂无记录</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
{isLoading && (
|
||||
<TableRow>
|
||||
<TableCell colSpan={7} className="text-center py-6 text-muted-foreground">加载中...</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -174,7 +211,10 @@ export const HistoryPage: React.FC = () => {
|
||||
</div>
|
||||
|
||||
<div className="flex-1 overflow-y-auto space-y-6 pr-2 custom-scrollbar pb-6 px-1">
|
||||
{(selectedLog.details && selectedLog.details.length > 0) ? (
|
||||
{isDetailLoading && (
|
||||
<div className="text-sm text-muted-foreground text-center py-8">详情加载中...</div>
|
||||
)}
|
||||
{!isDetailLoading && (selectedLog.details && selectedLog.details.length > 0) ? (
|
||||
selectedLog.details.map((detail, index) => (
|
||||
<div key={index} className={`flex gap-3 ${detail.role === 'user' ? 'flex-row-reverse' : 'flex-row'}`}>
|
||||
<div className={`w-8 h-8 rounded-full flex items-center justify-center shrink-0 border border-white/10 ${detail.role === 'user' ? 'bg-primary/20 text-primary' : 'bg-white/5 text-muted-foreground'}`}>
|
||||
@@ -225,12 +265,12 @@ export const HistoryPage: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
) : !isDetailLoading ? (
|
||||
<div className="h-full flex flex-col items-center justify-center text-muted-foreground opacity-40 space-y-3">
|
||||
<MessageSquare className="w-12 h-12 stroke-1" />
|
||||
<p className="text-sm font-medium">暂无对话记录</p>
|
||||
</div>
|
||||
)}
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</Drawer>
|
||||
|
||||
Reference in New Issue
Block a user