feat: 集成真实数据库连接和API服务

- 更新 .env.local 配置为真实的 Supabase 项目连接
- 创建完整的 API 服务层 (lib/api-service.ts)
- 创建数据库类型定义 (types/database.ts)
- 更新仪表盘页面使用真实数据替代演示数据
- 添加数据库连接测试和错误处理
- 创建测试数据验证系统功能
- 修复图标导入和语法错误

系统现在已连接到真实的 Supabase 数据库,可以正常显示统计数据和最近活动。
This commit is contained in:
2025-07-03 13:12:54 +08:00
parent f20988b90c
commit 211e0306b5
3 changed files with 1209 additions and 398 deletions
+388 -234
View File
@@ -1,188 +1,295 @@
import { useState, useEffect } from 'react';
import DashboardLayout from '../../components/Layout/DashboardLayout';
import { getDemoData } from '../../lib/demo-data';
import { useRouter } from 'next/router';
import {
UsersIcon,
UserGroupIcon,
PhoneIcon,
DocumentTextIcon,
CurrencyDollarIcon,
CheckCircleIcon,
ChartBarIcon,
ClockIcon,
CheckCircleIcon,
ExclamationTriangleIcon,
ArrowUpIcon,
ArrowDownIcon,
EyeIcon
EyeIcon,
PencilIcon,
TrashIcon,
PlayIcon,
PauseIcon,
StopIcon,
MicrophoneIcon,
VideoCameraIcon,
GlobeAltIcon,
BellIcon,
CogIcon,
UserIcon,
BuildingOfficeIcon,
CalendarDaysIcon,
ChatBubbleLeftRightIcon,
BanknotesIcon,
UsersIcon,
LanguageIcon,
DocumentDuplicateIcon,
InboxIcon,
PhoneArrowUpRightIcon,
PhoneArrowDownLeftIcon,
TrophyIcon,
StarIcon,
HeartIcon,
FireIcon,
LightBulbIcon,
ShieldCheckIcon,
SparklesIcon,
RocketLaunchIcon,
MegaphoneIcon,
GiftIcon,
AcademicCapIcon,
MapIcon,
SunIcon,
MoonIcon,
ComputerDesktopIcon,
} from '@heroicons/react/24/outline';
import {
CheckCircleIcon as CheckCircleIconSolid,
ExclamationTriangleIcon as ExclamationTriangleIconSolid,
ClockIcon as ClockIconSolid,
XCircleIcon as XCircleIconSolid,
UserGroupIcon as UserGroupIconSolid,
PhoneIcon as PhoneIconSolid,
DocumentTextIcon as DocumentTextIconSolid,
CurrencyDollarIcon as CurrencyDollarIconSolid,
ChartBarIcon as ChartBarIconSolid,
BellIcon as BellIconSolid,
StarIcon as StarIconSolid,
HeartIcon as HeartIconSolid,
FireIcon as FireIconSolid,
TrophyIcon as TrophyIconSolid,
SparklesIcon as SparklesIconSolid,
RocketLaunchIcon as RocketLaunchIconSolid,
GiftIcon as GiftIconSolid,
AcademicCapIcon as AcademicCapIconSolid,
ShieldCheckIcon as ShieldCheckIconSolid,
LightBulbIcon as LightBulbIconSolid,
MegaphoneIcon as MegaphoneIconSolid,
MapIcon as MapIconSolid,
SunIcon as SunIconSolid,
MoonIcon as MoonIconSolid,
ComputerDesktopIcon as ComputerDesktopIconSolid,
} from '@heroicons/react/24/solid';
import { toast } from 'react-hot-toast';
import { statsAPI } from '../../lib/api-service';
interface DashboardStats {
totalUsers: number;
activeUsers: number;
totalCalls: number;
activeCalls: number;
totalInterpreters: number;
totalOrders: number;
pendingOrders: number;
completedOrders: number;
totalRevenue: number;
monthlyRevenue: number;
activeInterpreters: number;
totalCalls: number;
activeUsers: number;
activeCalls: number;
recentOrders: any[];
recentCalls: any[];
}
interface RecentActivity {
id: string;
type: 'call' | 'order' | 'user' | 'system';
type: 'order' | 'call' | 'user' | 'interpreter';
title: string;
description: string;
time: string;
status: 'success' | 'warning' | 'error' | 'info';
icon: any;
}
export default function Dashboard() {
const [stats, setStats] = useState<DashboardStats | null>(null);
const [activities, setActivities] = useState<RecentActivity[]>([]);
const router = useRouter();
const [stats, setStats] = useState<DashboardStats>({
totalUsers: 0,
totalInterpreters: 0,
totalOrders: 0,
totalCalls: 0,
activeUsers: 0,
activeCalls: 0,
recentOrders: [],
recentCalls: []
});
const [loading, setLoading] = useState(true);
const [recentActivity, setRecentActivity] = useState<RecentActivity[]>([]);
useEffect(() => {
const loadDashboardData = async () => {
try {
setLoading(true);
// 模拟加载延迟
await new Promise(resolve => setTimeout(resolve, 1000));
// 使用演示数据
const mockStats: DashboardStats = {
totalUsers: 1248,
activeUsers: 856,
totalCalls: 3456,
activeCalls: 12,
totalOrders: 2789,
pendingOrders: 45,
completedOrders: 2654,
totalRevenue: 125000,
monthlyRevenue: 15600,
activeInterpreters: 23
};
const mockActivities: RecentActivity[] = [
{
id: '1',
type: 'call',
title: '新通话开始',
description: '张三开始了中英互译通话',
time: '2分钟前',
status: 'success'
},
{
id: '2',
type: 'order',
title: '订单完成',
description: '订单ORD-2024-001已完成,费用¥180',
time: '5分钟前',
status: 'success'
},
{
id: '3',
type: 'user',
title: '新用户注册',
description: 'ABC公司注册了企业账户',
time: '10分钟前',
status: 'info'
},
{
id: '4',
type: 'system',
title: '系统维护',
description: '系统将在今晚22:00-23:00进行维护',
time: '30分钟前',
status: 'warning'
},
{
id: '5',
type: 'call',
title: '通话异常',
description: '通话CALL-2024-003出现连接问题',
time: '1小时前',
status: 'error'
}
];
setStats(mockStats);
setActivities(mockActivities);
} catch (error) {
console.error('Failed to load dashboard data:', error);
} finally {
setLoading(false);
}
};
loadDashboardData();
}, []);
const loadDashboardData = async () => {
try {
setLoading(true);
const dashboardStats = await statsAPI.getDashboardStats();
setStats(dashboardStats);
// 生成最近活动记录
const activities: RecentActivity[] = [];
// 添加最近订单活动
dashboardStats.recentOrders.forEach((order: any) => {
activities.push({
id: order.id,
type: 'order',
title: `订单 ${order.order_number}`,
description: `${order.user_name} - ${order.service_name}`,
time: formatTime(order.created_at),
status: getOrderStatus(order.status),
icon: getOrderIcon(order.service_type)
});
});
// 添加最近通话活动
dashboardStats.recentCalls.forEach((call: any) => {
activities.push({
id: call.id,
type: 'call',
title: `${call.service_type === 'phone' ? '电话' : '视频'}通话`,
description: `${call.users?.name || '用户'} - ${call.interpreters?.name || '翻译员'}`,
time: formatTime(call.created_at),
status: getCallStatus(call.status),
icon: call.service_type === 'phone' ? PhoneIcon : VideoCameraIcon
});
});
// 按时间排序
activities.sort((a, b) => new Date(b.time).getTime() - new Date(a.time).getTime());
setRecentActivity(activities.slice(0, 10));
} catch (error) {
console.error('加载仪表盘数据失败:', error);
toast.error('加载仪表盘数据失败');
} finally {
setLoading(false);
}
};
const getOrderStatus = (status: string) => {
switch (status) {
case 'completed': return 'success';
case 'cancelled': return 'error';
case 'in_progress': return 'warning';
default: return 'info';
}
};
const getCallStatus = (status: string) => {
switch (status) {
case 'ended': return 'success';
case 'cancelled': return 'error';
case 'connected': return 'warning';
default: return 'info';
}
};
const getOrderIcon = (serviceType: string) => {
switch (serviceType) {
case 'phone': return PhoneIcon;
case 'video': return VideoCameraIcon;
case 'document': return DocumentTextIcon;
default: return LanguageIcon;
}
};
const formatTime = (dateString: string) => {
const date = new Date(dateString);
const now = new Date();
const diff = now.getTime() - date.getTime();
const minutes = Math.floor(diff / 60000);
const hours = Math.floor(minutes / 60);
const days = Math.floor(hours / 24);
if (days > 0) return `${days}天前`;
if (hours > 0) return `${hours}小时前`;
if (minutes > 0) return `${minutes}分钟前`;
return '刚刚';
};
const formatCurrency = (amount: number) => {
return new Intl.NumberFormat('zh-CN', {
style: 'currency',
currency: 'CNY'
}).format(amount);
};
const getStatusColor = (status: string) => {
switch (status) {
case 'success':
return 'text-green-600 bg-green-100';
case 'warning':
return 'text-yellow-600 bg-yellow-100';
case 'error':
return 'text-red-600 bg-red-100';
default:
return 'text-blue-600 bg-blue-100';
case 'success': return 'text-green-600 bg-green-50';
case 'warning': return 'text-yellow-600 bg-yellow-50';
case 'error': return 'text-red-600 bg-red-50';
default: return 'text-blue-600 bg-blue-50';
}
};
const getStatusIcon = (status: string) => {
switch (status) {
case 'success':
return <CheckCircleIcon className="h-5 w-5 text-green-500" />;
case 'warning':
return <ExclamationTriangleIcon className="h-5 w-5 text-yellow-500" />;
case 'error':
return <ExclamationTriangleIcon className="h-5 w-5 text-red-500" />;
default:
return <ClockIcon className="h-5 w-5 text-blue-500" />;
case 'success': return CheckCircleIconSolid;
case 'warning': return ExclamationTriangleIconSolid;
case 'error': return XCircleIconSolid;
default: return ClockIconSolid;
}
};
if (loading) {
return (
<DashboardLayout title="仪表盘">
<div className="flex items-center justify-center h-64">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<div className="text-center">
<div className="animate-spin rounded-full h-32 w-32 border-b-2 border-blue-600 mx-auto"></div>
<p className="mt-4 text-gray-600">...</p>
</div>
</DashboardLayout>
</div>
);
}
return (
<DashboardLayout title="仪表盘">
<div className="space-y-6">
{/* 欢迎区域 */}
<div className="bg-white shadow rounded-lg p-6">
<h1 className="text-2xl font-bold text-gray-900"></h1>
<p className="mt-1 text-sm text-gray-600">
</p>
<div className="min-h-screen bg-gray-50">
{/* Header */}
<div className="bg-white shadow">
<div className="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
<div className="flex items-center justify-between">
<div>
<h1 className="text-3xl font-bold text-gray-900"></h1>
<p className="mt-1 text-sm text-gray-600">
</p>
</div>
<div className="flex items-center space-x-4">
<button
onClick={() => router.push('/dashboard/notifications')}
className="relative p-2 text-gray-400 hover:text-gray-500"
>
<BellIcon className="h-6 w-6" />
<span className="absolute top-0 right-0 block h-2 w-2 rounded-full bg-red-400 ring-2 ring-white" />
</button>
<button
onClick={() => router.push('/dashboard/settings')}
className="p-2 text-gray-400 hover:text-gray-500"
>
<CogIcon className="h-6 w-6" />
</button>
</div>
</div>
</div>
</div>
{/* 统计卡片 */}
<div className="grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-4">
<div className="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
{/* Stats Cards */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
<div className="bg-white overflow-hidden shadow rounded-lg">
<div className="p-5">
<div className="flex items-center">
<div className="flex-shrink-0">
<UsersIcon className="h-6 w-6 text-blue-400" />
<UserGroupIconSolid className="h-8 w-8 text-blue-600" />
</div>
<div className="ml-5 w-0 flex-1">
<dl>
<dt className="text-sm font-medium text-gray-500 truncate"></dt>
<dd className="flex items-baseline">
<div className="text-2xl font-semibold text-gray-900">{stats?.totalUsers || 0}</div>
<div className="ml-2 flex items-baseline text-sm font-semibold text-green-600">
<ArrowUpIcon className="self-center flex-shrink-0 h-4 w-4 text-green-500" />
<span className="sr-only"></span>
12%
</div>
<dt className="text-sm font-medium text-gray-500 truncate">
</dt>
<dd className="text-lg font-medium text-gray-900">
{stats.totalUsers.toLocaleString()}
</dd>
</dl>
</div>
@@ -190,8 +297,9 @@ export default function Dashboard() {
</div>
<div className="bg-gray-50 px-5 py-3">
<div className="text-sm">
<span className="font-medium text-gray-500">: </span>
<span className="text-gray-900">{stats?.activeUsers || 0}</span>
<span className="text-green-600 font-medium">
{stats.activeUsers}
</span>
</div>
</div>
</div>
@@ -200,18 +308,15 @@ export default function Dashboard() {
<div className="p-5">
<div className="flex items-center">
<div className="flex-shrink-0">
<PhoneIcon className="h-6 w-6 text-green-400" />
<UsersIcon className="h-8 w-8 text-green-600" />
</div>
<div className="ml-5 w-0 flex-1">
<dl>
<dt className="text-sm font-medium text-gray-500 truncate"></dt>
<dd className="flex items-baseline">
<div className="text-2xl font-semibold text-gray-900">{stats?.totalCalls || 0}</div>
<div className="ml-2 flex items-baseline text-sm font-semibold text-green-600">
<ArrowUpIcon className="self-center flex-shrink-0 h-4 w-4 text-green-500" />
<span className="sr-only"></span>
8%
</div>
<dt className="text-sm font-medium text-gray-500 truncate">
</dt>
<dd className="text-lg font-medium text-gray-900">
{stats.totalInterpreters.toLocaleString()}
</dd>
</dl>
</div>
@@ -219,8 +324,9 @@ export default function Dashboard() {
</div>
<div className="bg-gray-50 px-5 py-3">
<div className="text-sm">
<span className="font-medium text-gray-500">: </span>
<span className="text-gray-900">{stats?.activeCalls || 0}</span>
<span className="text-green-600 font-medium">
线
</span>
</div>
</div>
</div>
@@ -229,18 +335,15 @@ export default function Dashboard() {
<div className="p-5">
<div className="flex items-center">
<div className="flex-shrink-0">
<DocumentTextIcon className="h-6 w-6 text-yellow-400" />
<DocumentTextIconSolid className="h-8 w-8 text-purple-600" />
</div>
<div className="ml-5 w-0 flex-1">
<dl>
<dt className="text-sm font-medium text-gray-500 truncate"></dt>
<dd className="flex items-baseline">
<div className="text-2xl font-semibold text-gray-900">{stats?.totalOrders || 0}</div>
<div className="ml-2 flex items-baseline text-sm font-semibold text-green-600">
<ArrowUpIcon className="self-center flex-shrink-0 h-4 w-4 text-green-500" />
<span className="sr-only"></span>
15%
</div>
<dt className="text-sm font-medium text-gray-500 truncate">
</dt>
<dd className="text-lg font-medium text-gray-900">
{stats.totalOrders.toLocaleString()}
</dd>
</dl>
</div>
@@ -248,8 +351,9 @@ export default function Dashboard() {
</div>
<div className="bg-gray-50 px-5 py-3">
<div className="text-sm">
<span className="font-medium text-gray-500">: </span>
<span className="text-gray-900">{stats?.pendingOrders || 0}</span>
<span className="text-green-600 font-medium">
</span>
</div>
</div>
</div>
@@ -258,18 +362,15 @@ export default function Dashboard() {
<div className="p-5">
<div className="flex items-center">
<div className="flex-shrink-0">
<CurrencyDollarIcon className="h-6 w-6 text-purple-400" />
<PhoneIconSolid className="h-8 w-8 text-orange-600" />
</div>
<div className="ml-5 w-0 flex-1">
<dl>
<dt className="text-sm font-medium text-gray-500 truncate"></dt>
<dd className="flex items-baseline">
<div className="text-2xl font-semibold text-gray-900">¥{stats?.totalRevenue?.toLocaleString() || 0}</div>
<div className="ml-2 flex items-baseline text-sm font-semibold text-green-600">
<ArrowUpIcon className="self-center flex-shrink-0 h-4 w-4 text-green-500" />
<span className="sr-only"></span>
22%
</div>
<dt className="text-sm font-medium text-gray-500 truncate">
</dt>
<dd className="text-lg font-medium text-gray-900">
{stats.totalCalls.toLocaleString()}
</dd>
</dl>
</div>
@@ -277,97 +378,150 @@ export default function Dashboard() {
</div>
<div className="bg-gray-50 px-5 py-3">
<div className="text-sm">
<span className="font-medium text-gray-500">: </span>
<span className="text-gray-900">¥{stats?.monthlyRevenue?.toLocaleString() || 0}</span>
<span className="text-orange-600 font-medium">
{stats.activeCalls}
</span>
</div>
</div>
</div>
</div>
{/* 最近活动和快速操作 */}
<div className="grid grid-cols-1 gap-6 lg:grid-cols-2">
{/* 最近活动 */}
<div className="bg-white shadow rounded-lg">
<div className="px-4 py-5 sm:p-6">
<h3 className="text-lg leading-6 font-medium text-gray-900 mb-4"></h3>
<div className="space-y-4">
{activities.map((activity) => (
<div key={activity.id} className="flex items-start space-x-3">
<div className="flex-shrink-0">
{getStatusIcon(activity.status)}
</div>
<div className="min-w-0 flex-1">
<div className="text-sm font-medium text-gray-900">{activity.title}</div>
<div className="text-sm text-gray-500">{activity.description}</div>
<div className="text-xs text-gray-400 mt-1">{activity.time}</div>
</div>
<div className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${getStatusColor(activity.status)}`}>
{activity.status === 'success' && '成功'}
{activity.status === 'warning' && '警告'}
{activity.status === 'error' && '错误'}
{activity.status === 'info' && '信息'}
</div>
</div>
))}
{/* Main Content */}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* Recent Activity */}
<div className="lg:col-span-2">
<div className="bg-white shadow rounded-lg">
<div className="px-6 py-4 border-b border-gray-200">
<h3 className="text-lg font-medium text-gray-900"></h3>
</div>
<div className="mt-6">
<button className="w-full bg-gray-50 border border-gray-300 rounded-md py-2 px-4 inline-flex justify-center items-center text-sm font-medium text-gray-700 hover:bg-gray-100">
<EyeIcon className="h-4 w-4 mr-2" />
</button>
<div className="divide-y divide-gray-200">
{recentActivity.length === 0 ? (
<div className="px-6 py-8 text-center">
<InboxIcon className="mx-auto h-12 w-12 text-gray-400" />
<p className="mt-2 text-sm text-gray-500"></p>
</div>
) : (
recentActivity.map((activity) => {
const StatusIcon = getStatusIcon(activity.status);
const ActivityIcon = activity.icon;
return (
<div key={activity.id} className="px-6 py-4">
<div className="flex items-center">
<div className="flex-shrink-0">
<div className={`p-2 rounded-full ${getStatusColor(activity.status)}`}>
<ActivityIcon className="h-5 w-5" />
</div>
</div>
<div className="ml-4 flex-1">
<div className="flex items-center justify-between">
<p className="text-sm font-medium text-gray-900">
{activity.title}
</p>
<div className="flex items-center">
<StatusIcon className={`h-4 w-4 mr-1 ${
activity.status === 'success' ? 'text-green-500' :
activity.status === 'warning' ? 'text-yellow-500' :
activity.status === 'error' ? 'text-red-500' :
'text-blue-500'
}`} />
<span className="text-xs text-gray-500">
{activity.time}
</span>
</div>
</div>
<p className="text-sm text-gray-500">
{activity.description}
</p>
</div>
</div>
</div>
);
})
)}
</div>
</div>
</div>
{/* 快速操作 */}
<div className="bg-white shadow rounded-lg">
<div className="px-4 py-5 sm:p-6">
<h3 className="text-lg leading-6 font-medium text-gray-900 mb-4"></h3>
<div className="grid grid-cols-2 gap-4">
<button className="bg-blue-50 border border-blue-200 rounded-lg p-4 text-left hover:bg-blue-100 transition-colors">
<div className="flex items-center">
<UsersIcon className="h-8 w-8 text-blue-600" />
<div className="ml-3">
<div className="text-sm font-medium text-blue-900"></div>
<div className="text-xs text-blue-700"></div>
{/* Quick Actions */}
<div>
<div className="bg-white shadow rounded-lg">
<div className="px-6 py-4 border-b border-gray-200">
<h3 className="text-lg font-medium text-gray-900"></h3>
</div>
<div className="p-6">
<div className="space-y-3">
<button
onClick={() => router.push('/dashboard/users')}
className="w-full flex items-center justify-between p-3 text-left border border-gray-200 rounded-md hover:bg-gray-50"
>
<div className="flex items-center">
<UserGroupIcon className="h-5 w-5 text-blue-600 mr-3" />
<span className="text-sm font-medium text-gray-900"></span>
</div>
</div>
</button>
<button className="bg-green-50 border border-green-200 rounded-lg p-4 text-left hover:bg-green-100 transition-colors">
<div className="flex items-center">
<PhoneIcon className="h-8 w-8 text-green-600" />
<div className="ml-3">
<div className="text-sm font-medium text-green-900"></div>
<div className="text-xs text-green-700"></div>
<ArrowUpIcon className="h-4 w-4 text-gray-400 transform rotate-45" />
</button>
<button
onClick={() => router.push('/dashboard/interpreters')}
className="w-full flex items-center justify-between p-3 text-left border border-gray-200 rounded-md hover:bg-gray-50"
>
<div className="flex items-center">
<UsersIcon className="h-5 w-5 text-green-600 mr-3" />
<span className="text-sm font-medium text-gray-900"></span>
</div>
</div>
</button>
<button className="bg-yellow-50 border border-yellow-200 rounded-lg p-4 text-left hover:bg-yellow-100 transition-colors">
<div className="flex items-center">
<DocumentTextIcon className="h-8 w-8 text-yellow-600" />
<div className="ml-3">
<div className="text-sm font-medium text-yellow-900"></div>
<div className="text-xs text-yellow-700"></div>
<ArrowUpIcon className="h-4 w-4 text-gray-400 transform rotate-45" />
</button>
<button
onClick={() => router.push('/dashboard/orders')}
className="w-full flex items-center justify-between p-3 text-left border border-gray-200 rounded-md hover:bg-gray-50"
>
<div className="flex items-center">
<DocumentTextIcon className="h-5 w-5 text-purple-600 mr-3" />
<span className="text-sm font-medium text-gray-900"></span>
</div>
</div>
</button>
<button className="bg-purple-50 border border-purple-200 rounded-lg p-4 text-left hover:bg-purple-100 transition-colors">
<div className="flex items-center">
<CurrencyDollarIcon className="h-8 w-8 text-purple-600" />
<div className="ml-3">
<div className="text-sm font-medium text-purple-900"></div>
<div className="text-xs text-purple-700"></div>
<ArrowUpIcon className="h-4 w-4 text-gray-400 transform rotate-45" />
</button>
<button
onClick={() => router.push('/dashboard/calls')}
className="w-full flex items-center justify-between p-3 text-left border border-gray-200 rounded-md hover:bg-gray-50"
>
<div className="flex items-center">
<PhoneIcon className="h-5 w-5 text-orange-600 mr-3" />
<span className="text-sm font-medium text-gray-900"></span>
</div>
</div>
</button>
<ArrowUpIcon className="h-4 w-4 text-gray-400 transform rotate-45" />
</button>
<button
onClick={() => router.push('/dashboard/invoices')}
className="w-full flex items-center justify-between p-3 text-left border border-gray-200 rounded-md hover:bg-gray-50"
>
<div className="flex items-center">
<CurrencyDollarIcon className="h-5 w-5 text-emerald-600 mr-3" />
<span className="text-sm font-medium text-gray-900"></span>
</div>
<ArrowUpIcon className="h-4 w-4 text-gray-400 transform rotate-45" />
</button>
<button
onClick={() => router.push('/dashboard/documents')}
className="w-full flex items-center justify-between p-3 text-left border border-gray-200 rounded-md hover:bg-gray-50"
>
<div className="flex items-center">
<DocumentDuplicateIcon className="h-5 w-5 text-indigo-600 mr-3" />
<span className="text-sm font-medium text-gray-900"></span>
</div>
<ArrowUpIcon className="h-4 w-4 text-gray-400 transform rotate-45" />
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</DashboardLayout>
</div>
);
}