feat: restyle StatusView and status page with Tailwind
This commit is contained in:
@@ -8,9 +8,12 @@ export default async function StatusPage({
|
|||||||
}) {
|
}) {
|
||||||
const { locale, uuid } = await params
|
const { locale, uuid } = await params
|
||||||
const t = await getTranslations({ locale, namespace: 'status' })
|
const t = await getTranslations({ locale, namespace: 'status' })
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main style={{ padding: '2rem' }}>
|
<main className="flex flex-col items-center px-6 py-16">
|
||||||
<h1>{t('heading')}</h1>
|
<h1 className="text-2xl font-bold tracking-tight text-gray-900 dark:text-gray-50 mb-10">
|
||||||
|
{t('heading')}
|
||||||
|
</h1>
|
||||||
<StatusView uuid={uuid} />
|
<StatusView uuid={uuid} />
|
||||||
</main>
|
</main>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -15,6 +15,15 @@ type DownloadData = {
|
|||||||
tokenExpiresAt: string | null
|
tokenExpiresAt: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Spinner() {
|
||||||
|
return (
|
||||||
|
<svg className="animate-spin w-8 h-8 text-violet-600 dark:text-violet-400" viewBox="0 0 24 24" fill="none" aria-hidden>
|
||||||
|
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
|
||||||
|
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" />
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export function StatusView({ uuid }: { uuid: string }) {
|
export function StatusView({ uuid }: { uuid: string }) {
|
||||||
const t = useTranslations('status')
|
const t = useTranslations('status')
|
||||||
const locale = useLocale()
|
const locale = useLocale()
|
||||||
@@ -45,14 +54,33 @@ export function StatusView({ uuid }: { uuid: string }) {
|
|||||||
return () => { active = false }
|
return () => { active = false }
|
||||||
}, [uuid])
|
}, [uuid])
|
||||||
|
|
||||||
if (notFound) return <p>{t('notFound')}</p>
|
const cardClass = 'max-w-md w-full mx-auto rounded-2xl border border-gray-100 dark:border-slate-800 bg-gray-50 dark:bg-slate-900 p-8'
|
||||||
if (!data) return <p>{t('loading')}</p>
|
|
||||||
|
if (notFound) {
|
||||||
|
return (
|
||||||
|
<div className={cardClass}>
|
||||||
|
<p className="text-gray-600 dark:text-gray-400 text-center">{t('notFound')}</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
return (
|
||||||
|
<div className={`${cardClass} flex flex-col items-center gap-4`}>
|
||||||
|
<Spinner />
|
||||||
|
<p className="text-gray-500 dark:text-gray-400 text-sm">{t('loading')}</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (data.status === 'PENDING' || data.status === 'PROCESSING') {
|
if (data.status === 'PENDING' || data.status === 'PROCESSING') {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className={`${cardClass} flex flex-col items-center gap-4`}>
|
||||||
<p>{data.status === 'PENDING' ? t('pending') : t('processing')}</p>
|
<Spinner />
|
||||||
<p>{t('polling')}</p>
|
<p className="font-medium text-gray-900 dark:text-gray-50">
|
||||||
|
{data.status === 'PENDING' ? t('pending') : t('processing')}
|
||||||
|
</p>
|
||||||
|
<p className="text-sm text-gray-500 dark:text-gray-400 text-center">{t('polling')}</p>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -62,16 +90,24 @@ export function StatusView({ uuid }: { uuid: string }) {
|
|||||||
? `(${(Number(data.fileSize) / 1_048_576).toFixed(1)} MB)`
|
? `(${(Number(data.fileSize) / 1_048_576).toFixed(1)} MB)`
|
||||||
: ''
|
: ''
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className={`${cardClass} flex flex-col items-center gap-5`}>
|
||||||
<p>{t('ready')} {sizeMb}</p>
|
<div className="w-12 h-12 rounded-full bg-green-100 dark:bg-green-900/30 flex items-center justify-center">
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="text-green-600 dark:text-green-400" aria-hidden>
|
||||||
|
<polyline points="20 6 9 17 4 12"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<p className="font-medium text-gray-900 dark:text-gray-50">
|
||||||
|
{t('ready')} {sizeMb}
|
||||||
|
</p>
|
||||||
<a
|
<a
|
||||||
href={`/api/download/${data.downloadToken}`}
|
href={`/api/download/${data.downloadToken}`}
|
||||||
download={data.fileName ?? undefined}
|
download={data.fileName ?? undefined}
|
||||||
|
className="w-full text-center rounded-xl bg-violet-600 hover:bg-violet-700 dark:bg-violet-500 dark:hover:bg-violet-600 text-white font-medium py-3 transition-colors"
|
||||||
>
|
>
|
||||||
{t('download')} {data.fileName}
|
{t('download')} {data.fileName}
|
||||||
</a>
|
</a>
|
||||||
{data.tokenExpiresAt && (
|
{data.tokenExpiresAt && (
|
||||||
<p style={{ fontSize: '0.85rem', color: '#666' }}>
|
<p className="text-xs text-gray-400 dark:text-slate-500">
|
||||||
{t('linkExpires')}{' '}
|
{t('linkExpires')}{' '}
|
||||||
{new Date(data.tokenExpiresAt).toLocaleString(locale)}
|
{new Date(data.tokenExpiresAt).toLocaleString(locale)}
|
||||||
</p>
|
</p>
|
||||||
@@ -82,9 +118,9 @@ export function StatusView({ uuid }: { uuid: string }) {
|
|||||||
|
|
||||||
if (data.status === 'FAILED') {
|
if (data.status === 'FAILED') {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className={cardClass}>
|
||||||
<p>{t('failed')}</p>
|
<p className="font-medium text-red-600 dark:text-red-400 mb-3">{t('failed')}</p>
|
||||||
<pre style={{ background: '#fee', padding: '0.5rem', overflowX: 'auto' }}>
|
<pre className="text-xs bg-red-50 dark:bg-red-950/40 border border-red-100 dark:border-red-900/50 rounded-lg p-4 overflow-x-auto text-red-700 dark:text-red-300 whitespace-pre-wrap">
|
||||||
{data.errorMsg}
|
{data.errorMsg}
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
@@ -92,8 +128,16 @@ export function StatusView({ uuid }: { uuid: string }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (data.status === 'FILE_DELETED') {
|
if (data.status === 'FILE_DELETED') {
|
||||||
return <p>{t('deleted')}</p>
|
return (
|
||||||
|
<div className={cardClass}>
|
||||||
|
<p className="text-gray-600 dark:text-gray-400 text-center">{t('deleted')}</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return <p>{t('unknown')}: {data.status}</p>
|
return (
|
||||||
|
<div className={cardClass}>
|
||||||
|
<p className="text-gray-500 dark:text-gray-400 text-center">{t('unknown')}: {data.status}</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user