accounts/dashboard/components/download/FileTable.tsx

96 lines
3.5 KiB
TypeScript

'use client'
import { useMemo, useState } from 'react'
import Breadcrumbs, { Crumb } from './Breadcrumbs'
import CopyButton from './CopyButton'
import { useLanguage } from '@i18n/LanguageProvider'
import { translations } from '@i18n/translations'
import { formatBytes, formatDate } from '../../lib/format'
import type { DirListing } from '../../types/download'
interface FileTableProps {
listing: DirListing
breadcrumb: Crumb[]
showBreadcrumbs?: boolean
}
export default function FileTable({ listing, breadcrumb, showBreadcrumbs = true }: FileTableProps) {
const { language } = useLanguage()
const locale = language === 'zh' ? 'zh-CN' : 'en-US'
const t = translations[language].download.fileTable
const copyLabel = translations[language].download.copyButton.tooltip
const [sort, setSort] = useState<'name' | 'lastModified' | 'size'>('name')
const [ext, setExt] = useState('')
const filtered = useMemo(() => {
return listing.entries
.filter((item) => !ext || item.name.toLowerCase().endsWith(ext.toLowerCase()))
.sort((a, b) => {
switch (sort) {
case 'lastModified':
return new Date(b.lastModified || 0).getTime() - new Date(a.lastModified || 0).getTime()
case 'size':
return (b.size || 0) - (a.size || 0)
default:
return a.name.localeCompare(b.name, locale)
}
})
}, [listing.entries, sort, ext, locale])
return (
<div>
{showBreadcrumbs && <Breadcrumbs items={breadcrumb} />}
<div className="mb-2 flex flex-wrap gap-2">
<select className="rounded border p-2" value={sort} onChange={(event) => setSort(event.target.value as any)}>
<option value="name">{t.sortName}</option>
<option value="lastModified">{t.sortUpdated}</option>
<option value="size">{t.sortSize}</option>
</select>
<input
className="rounded border p-2"
placeholder={t.filterPlaceholder}
value={ext}
onChange={(event) => setExt(event.target.value)}
/>
</div>
<table className="w-full text-sm">
<thead>
<tr className="border-b">
<th className="py-2 text-left">{t.headers.name}</th>
<th className="w-24 py-2 text-left">{t.headers.size}</th>
<th className="w-48 py-2 text-left">{t.headers.updated}</th>
<th className="w-40 py-2 text-left">{t.headers.actions}</th>
</tr>
</thead>
<tbody>
{filtered.map((item) => {
const downloadUrl = item.href.startsWith('http') ? item.href : `https://dl.svc.plus${item.href}`
return (
<tr key={item.name} className="border-b last:border-0">
<td className="py-1">
<a
href={downloadUrl}
target="_blank"
rel="noopener noreferrer"
className="text-blue-600 hover:underline"
>
{item.name}
</a>
</td>
<td className="py-1">{formatBytes(item.size || 0)}</td>
<td className="py-1">{item.lastModified ? formatDate(item.lastModified, locale) : '--'}</td>
<td className="py-1">
<div className="flex flex-wrap gap-2">
<CopyButton text={downloadUrl} label={copyLabel} />
</div>
</td>
</tr>
)
})}
</tbody>
</table>
</div>
)
}