feat: add ApkTable component

This commit is contained in:
2026-03-04 21:33:22 +08:00
parent e217d1c6c9
commit bf63f52b66
2 changed files with 113 additions and 0 deletions

75
app/download/ApkTable.tsx Normal file
View File

@@ -0,0 +1,75 @@
'use client';
import { formatFileSize, formatDateTime } from '@/lib/utils';
interface Apk {
name: string;
commit: string | null;
size: number;
modifiedAt: string;
downloadUrl: string;
}
interface ApkTableProps {
apks: Apk[];
}
export default function ApkTable({ apks }: ApkTableProps) {
if (!apks || apks.length === 0) {
return (
<div className="bg-white rounded-lg shadow-md p-12 text-center">
<p className="text-gray-500"> APK </p>
</div>
);
}
return (
<div className="bg-white rounded-lg shadow-md overflow-hidden">
<table className="w-full">
<thead className="bg-gray-50 border-b border-gray-200">
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Commit ID
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
</th>
<th className="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">
</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{apks.map((apk) => (
<tr key={apk.name} className="hover:bg-gray-50">
<td className="px-6 py-4 whitespace-nowrap">
<span className="font-mono text-sm text-gray-900">
{apk.commit || '-'}
</span>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
{formatFileSize(apk.size)}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
{formatDateTime(new Date(apk.modifiedAt))}
</td>
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<a
href={apk.downloadUrl}
download
rel="noopener noreferrer"
className="inline-flex items-center px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 transition-colors"
>
</a>
</td>
</tr>
))}
</tbody>
</table>
</div>
);
}

View File

@@ -0,0 +1,38 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import ApkTable from '../ApkTable';
describe('ApkTable', () => {
const mockApks = [
{
name: 'ft_timeshift_0_42_57ef3a60d.apk',
commit: '57ef3a60d',
size: 15728640,
modifiedAt: '2026-03-04T14:30:00.000Z',
downloadUrl: '/api/download?project=FT&version=0_42&filename=ft_timeshift_0_42_57ef3a60d.apk',
},
];
it('should render table with correct columns', () => {
render(<ApkTable apks={mockApks} />);
expect(screen.getByText('Commit ID')).toBeInTheDocument();
expect(screen.getByText('文件大小')).toBeInTheDocument();
expect(screen.getByText('创建时间')).toBeInTheDocument();
});
it('should render APK data correctly', () => {
render(<ApkTable apks={mockApks} />);
expect(screen.getByText('57ef3a60d')).toBeInTheDocument();
expect(screen.getByText('15.00 MB')).toBeInTheDocument();
expect(screen.getByText('2026-03-04 22:30')).toBeInTheDocument();
expect(screen.getByText('下载')).toBeInTheDocument();
});
it('should show empty state when no APKs', () => {
render(<ApkTable apks={[]} />);
expect(screen.getByText('暂无 APK 文件')).toBeInTheDocument();
});
});